Notification hooks
These hooks read the notifications in client.notifications and publish new ones from a component. They're exported from stream-chat-react.
What a notification holds, and who publishes which, is on Publishing notifications. How NotificationList shows them, and how a notification reaches a panel, is on Displaying notifications.
Each entry gives the signature, then:
- Returns: what the hook gives back.
- Reads: the store or the context it reads.
- Re-renders: what makes the component render again.
- Needs: where the component must sit, and what the hook does outside that place.
useNotifications
Returns the notifications in client.notifications, narrowed by the options.
useNotifications(options?: UseNotificationsOptions): Notification[]The options, each optional:
-
panel: keep only the notifications routed to this panel,'channel','thread','channel-list','thread-list'or'modal'. Routing notifications explains the routing. -
fallbackPanel: the panel a notification without a panel tag belongs to.'channel'by default. -
applyDisplayFilter: also apply the display filter ofChator of the nearestNotificationConfigurationProvider, asNotificationListdoes. -
filter:(notification) => boolean, applied last. -
Returns: the notifications that pass, in the order of the store. Without options, every notification, system notifications included. System notifications carry no panel tag, so they also pass for the
fallbackPanel,'channel'unless you set it: drop them withhasSystemNotificationTag. -
Reads:
client.notifications.store, fieldnotifications. -
Re-renders: whenever a notification is added or removed, even one the options drop. A new
filterfunction subscribes again, so define it at module scope, as in Selector identity. -
Needs:
Chat. Throws outside it.
A notification you render this way leaves only when its countdown runs. Start it with ensureNotificationTimeout(id) from useNotificationApi once it's on screen:
import { useEffect } from "react";
import type { Notification } from "stream-chat";
import {
hasSystemNotificationTag,
useNotificationApi,
useNotifications,
} from "stream-chat-react";
const isPanelNotification = (notification: Notification) =>
!hasSystemNotificationTag(notification);
export const ChannelToasts = () => {
const notifications = useNotifications({
filter: isPanelNotification,
panel: "channel",
});
const { ensureNotificationTimeout, removeNotification } =
useNotificationApi();
useEffect(() => {
notifications.forEach(({ id }) => ensureNotificationTimeout(id));
}, [ensureNotificationTimeout, notifications]);
return (
<ul className="toasts">
{notifications.map(({ id, message }) => (
<li key={id}>
{message}
<button onClick={() => removeNotification(id)} type="button">
Close
</button>
</li>
))}
</ul>
);
};useSystemNotifications
Returns the system notifications: the ones about the whole app, such as "Reconnecting…".
useSystemNotifications(options?: UseSystemNotificationsOptions): Notification[]- Returns: the notifications tagged
system, narrowed byoptions.filterwhen you pass one.Chatpublishes the lost-connection notification this way, and noNotificationListshows it. - Reads:
client.notifications.store, fieldnotifications. - Re-renders: whenever a notification is added or removed. Define
filterat module scope, as foruseNotifications. - Needs:
Chat. Throws outside it.
System notifications renders a connection banner with it, and the System notification banner recipe builds a complete one.
useNotificationApi
Returns the functions a component publishes and removes notifications with.
useNotificationApi(): NotificationApi- Returns:
addNotification(params): publishes a notification tagged for the panel the component sits in, the oneuseNotificationTargetreturns. While a modal is open, it also tags it for themodalpanel. Returns nothing.addSystemNotification(params): publishes a notification taggedsystemand for no panel. Returns its id.removeNotification(id): removes a notification.startNotificationTimeout(id): starts a notification's countdown, from the beginning if it's already running.ensureNotificationTimeout(id): starts the countdown unless it's already running.
- Reads: the panel from
useNotificationTarget(), and whether a modal is open from the modal dialog manager. - Re-renders: when the component's panel changes, and when a modal opens or closes.
- Needs:
Chat. Throws outside it.
addNotification takes:
| Param | What it does |
|---|---|
message |
the text to show. Required. |
emitter |
who published it, such as the component's name. Required. |
severity |
'error', 'warning', 'info', 'success', or a string of your own such as 'loading'. The first four have a default duration. |
type |
what the notification is about, such as 'api:message:pin:success'. Translators and filters match on it. |
incident |
{ domain, entity, operation, status? }, used to build type when you don't pass one. status defaults to failed for an error, success for a success, else the severity. |
duration |
milliseconds before it leaves, counted from when it's shown. |
actions |
buttons, each { label, handler }. |
context |
data stored as origin.context, such as the message it's about. |
error |
the error that caused it, stored as originalError. |
tags |
tags added next to the panel tags. |
targetPanels |
the panels to show it in, instead of the component's own. |
ariaLive |
'polite' or 'assertive': how urgently screen readers announce it. By default assertive for an error, polite otherwise. |
addSystemNotification takes the same, without targetPanels.
import { useNotificationApi } from "stream-chat-react";
const exportIncident = { domain: "api", entity: "chat", operation: "export" };
export const ExportButton = ({
exportChat,
}: {
exportChat: () => Promise<void>;
}) => {
const { addNotification } = useNotificationApi();
const onClick = async () => {
try {
await exportChat();
// Published as api:chat:export:success
addNotification({
emitter: "ExportButton",
incident: exportIncident,
message: "Chat exported",
severity: "success",
});
} catch (error) {
// Published as api:chat:export:failed
addNotification({
emitter: "ExportButton",
error: error instanceof Error ? error : undefined,
incident: exportIncident,
message: "The chat could not be exported",
severity: "error",
});
}
};
return (
<button onClick={onClick} type="button">
Export
</button>
);
};Outside Chat, or from code that isn't a component, publish with client.notifications directly, as Publishing a notification shows. Those notifications carry no panel tag.
useNotificationTarget
Returns the panel the component sits in.
useNotificationTarget(): NotificationTargetPanel | undefined- Returns, the first that applies:
'thread'inside aThread;'channel'inside aChannel;'thread-list'while the layout shows the threads view;'channel-list'insideChannelLists;undefined.
- Reads: the
Thread,Channel, workspace navigation and channel list contexts. No store. - Re-renders: when one of those contexts changes.
- Needs: nothing. Never throws.
MessageList uses it to pick the panel of its notification list. A list of your own can do the same:
import { NotificationList, useNotificationTarget } from "stream-chat-react";
export const PanelNotifications = () => {
const panel = useNotificationTarget();
return <NotificationList panel={panel ?? "channel"} />;
};