Notifications
Notifications are the short messages the SDK shows after something happens: "Message pinned" after a pin, "Error uploading attachment" after a failed upload. This page covers what users see and when, and points to the pages that explain each part.
What users get
The user pins a message in a channel:
- "Message pinned" animates in at the bottom of the message list, with a check icon.
- Three seconds after it appears, it animates out.
- On Android, the notification is a live region, so TalkBack reads it when it appears.
If the pin fails, an error notification appears in the same place: the error's message, or "Error pinning message" when the error has none.
What users see follows a few rules:
- Where. A notification appears in the part of the screen where it happened: the channel, the thread, the channel list, the thread list, or a channel details screen.
- One at a time. Each part of the screen shows one notification: the newest one, or a notification without a duration ahead of any other. The list removes the others. There is no queue.
- How long. A notification stays three seconds by default, counted from when it appears, and one with action buttons stays at least five. One without a duration stays until the user closes it, and shows a close button.
- Until the user leaves. When the user leaves a screen, for example by going back from a channel, the notifications for that screen are removed.
- In the user's language. The text is translated with the rest of the UI.
A lost connection is different. It isn't a notification. The message list and the channel list show a "Reconnecting..." bar of their own. Connection status covers it.
How it works across the layers
- State. Every notification lives in
client.notifications, the client's notification manager. It holds the list inclient.notifications.store, and a default duration for each severity:error,warning,infoandsuccess.stream-chatpublishes there itself when an upload, a send, an edit, a poll vote or a jump to a message goes wrong. Each notification has atype, such asapi:attachment:upload:failed, that says what it's about. Publishing notifications explains the manager, the types and the full list of whatstream-chatpublishes. - UI. The SDK's components publish with
useNotificationApi(): message actions, channel actions, member actions, the channel details lists, polls, voice recording, audio playback and reactions. The hook tags each notification for the part of the screen the component sits in, its notification target. ANotificationListserves each target: theMessageListorMessageFlashListof a channel or a thread, theChannelList, theThreadListand each channel details screen. A list shows the notifications tagged for its target, one at a time, and starts each one's countdown when it shows it.Notificationrenders one notification: its icon, its translated text, its action buttons and a close button. - Navigation. Not involved. A notification follows the component that published it: a pin inside a
Threadgoes to that thread's list, on whichever screen the thread is.
A notification tagged system is shown by no list. The app renders it with useSystemNotifications().
Displaying notifications explains the targets and how notifications reach them, translation, system notifications, and what each component publishes.
Turning it on or off
Notifications are always on, and nothing in client.config or the dashboard turns them off. What a user can trigger depends on their capabilities. A user who can't pin messages has no Pin action, so never sees "Message pinned".
To show none, replace the Notification component with one that renders nothing. Every list renders it, including the lists of the channel details screens, so the lists still start each countdown and remove each notification:
import type { PropsWithChildren } from "react";
import { WithComponents } from "stream-chat-react-native";
import type { ComponentOverrides } from "stream-chat-react-native";
// The lists keep running, but render nothing.
const overrides: ComponentOverrides = { Notification: () => null };
export const WithoutNotifications = ({ children }: PropsWithChildren) => (
<WithComponents overrides={overrides}>{children}</WithComponents>
);Replacing NotificationList with a component that renders nothing hides the notifications of the message lists, ChannelList and ThreadList, but not those of the channel details screens, which render the default list directly. No countdown starts in a replaced list, so its notifications stay in client.notifications.store until your code removes them.
Customizing
Publishing your own
Call addNotification from useNotificationApi() in a component inside Chat. The notification goes to the target the component sits in:
import { Pressable, Text } from "react-native";
import { useNotificationApi } from "stream-chat-react-native";
export const FavoriteButton = ({
onFavorite,
}: {
onFavorite: () => Promise<void>;
}) => {
const { addNotification } = useNotificationApi();
const favorite = async () => {
try {
await onFavorite();
addNotification({
message: "Added to favorites",
options: { severity: "success", type: "app:channel:favorite:success" },
origin: { emitter: "FavoriteButton" },
});
} catch (error) {
addNotification({
message: "Could not add to favorites",
options: {
originalError: error instanceof Error ? error : undefined,
severity: "error",
type: "app:channel:favorite:failed",
},
origin: { emitter: "FavoriteButton" },
});
}
};
return (
<Pressable accessibilityRole="button" onPress={favorite}>
<Text>Favorite</Text>
</Pressable>
);
};The message is shown as you pass it, so translate it first. Routing notifications shows how to send it to another target. Outside React, publish with client.notifications.add(), as in Publishing a notification.
How long notifications stay
The durations are client settings, set per severity. Naming one keeps the others at three seconds:
client.config.set({
client: { notifications: { durations: { error: 8000 } } },
});Configuration explains client.config, and Severity, actions and durations covers the duration of one notification and notifications that stay.
Where and how they look
- Move the lists to the top of the screen, or change how each notification renders: Replacing the list or a notification.
- Change colors and spacing with the theme: Styling.
- Keep some notifications out of a list: pass a
filterto yourNotificationList, as Notification lists and targets explains. - Render them in a snackbar of your own: the Snackbar UI cookbook.
What they say
The text of a built-in notification comes from a translation key. Override the key the component published with, or the notifications.* key of a stream-chat notification, as Translating notifications explains.
What screen readers hear
The default Notification is an Android live region: assertive for errors, polite for the rest. VoiceOver has no live regions, and the SDK's announcer doesn't read notifications. To have a notification read on both platforms, announce it yourself with useAccessibilityAnnouncer(), as Accessibility shows.
System notifications
A notification about the whole app, rather than one screen, is a system notification. Publish it with addSystemNotification, and render it where you want with useSystemNotifications(), as System notifications shows.
Reference
- Publishing notifications:
client.notifications, the notification types and whatstream-chatpublishes. - Displaying notifications: the
NotificationListandNotificationprops, targets, and what the SDK's components publish. - Notification hooks:
useNotificationApi,useNotifications,useSystemNotificationsanduseNotificationListController. - Notification components:
NotificationandNotificationIcon. - Notification list:
NotificationListandNotificationTargetProvider. - ComponentsContext: the
Notification,NotificationListandNotificationIconoverride keys. - Configuration tree:
client.notifications.durations.