Notification hooks
These hooks read the notifications in client.notifications, publish new ones from a component, and drive a notification list. They're exported from stream-chat-react-native and stream-chat-expo.
What a notification holds, and who publishes which, is on Publishing notifications. How NotificationList shows them, and how a notification reaches a list through its target, 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?: {
filter?: (notification: Notification) => boolean;
requireTarget?: boolean;
target?: NotificationTarget;
}): Notification[]The options, each optional:
-
target:{ panel, hostId }. Keep only the notifications for that host, the ones aNotificationListserving it shows. A notification is for the host when it has a target tag naming the host, or a tag naming only its panel, or anorigin.context.panelset to its panel. Without any of these, a composer in itsorigin.contextthat belongs to the host also makes it the host's. Routing notifications explains the tags. -
requireTarget: return[]whiletargetisundefined, instead of every notification. -
filter: applied last. -
Returns: the notifications that pass, in the order of the store. Without
target, every notification in the store, system notifications included. The hook's JSDoc says it returns non-system notifications, but it doesn't filter them out: drop them withhasSystemNotificationTagin yourfilter. With atarget, system notifications drop out, because they carry no target tag. -
Reads:
client.notifications.store, fieldnotifications. -
Re-renders: whenever a notification is added, removed or changed, even one the options drop. A new
filterfunction or a newtargetobject subscribes again, so define the filter at module scope and keep the target's identity, as in Selector identity. -
Needs:
Chat. Throws outside it.
A notification you render this way leaves only when its countdown runs, or when you remove it. Start the countdown with startNotificationTimeout(id) from useNotificationApi once it's on screen, or use useNotificationListController, which does that for you.
useSystemNotifications
Returns the system notifications: the ones about the whole app, not one part of it.
useSystemNotifications(options?: {
filter?: (notification: Notification) => boolean;
}): Notification[]- Returns: the notifications tagged
system, narrowed byoptions.filterwhen you pass one. NoNotificationListshows them. - Reads:
client.notifications.store, fieldnotifications. - Re-renders: whenever a notification is added, removed or changed. Define
filterat module scope, as foruseNotifications. - Needs:
Chat. Throws outside it.
The SDK publishes no system notification of its own: its connection indicators read the connection stores. System notifications publishes one and renders it with this hook.
useNotificationApi
Returns the functions a component publishes and removes notifications with.
useNotificationApi(): NotificationApi- Returns:
addNotification(payload, options?): publishes a notification tagged for the target the component sits in, the target of the nearestNotificationTargetProvider.Channel,Thread,ChannelList,ThreadListand the channel details screens each set one. Returns nothing.addSystemNotification(payload, options?): publishes a notification taggedsystemand for no target. Returns its id.removeNotification(id): removes a notification.removeNotificationsForCurrentPanel(): removes every notification, other than system ones, for the component's target.startNotificationTimeout(id, durationOverride?): starts a notification's countdown, from the beginning if it's already running.durationOverridereplaces its duration. Without a duration or adurationOverride, there's no countdown.runWithNotificationTarget(callback, target?): runscallback, and tags the notifications published without a target while it runs fortarget, by default the component's own. Returns the callback's result as a promise.
- Reads: the target from the nearest
NotificationTargetProvider. No store. - Re-renders: never by itself. The functions change identity when the component's target changes.
- Needs:
Chat. Throws outside it. Outside every target,addNotificationpublishes without a target tag, and the notification goes to the list that mounted last.
payload is what client.notifications.add() takes: { message, origin: { emitter, context? }, options? }, where options holds severity, type, duration, actions, metadata, tags and originalError. Publishing a notification describes each field.
options, the second argument of addNotification, takes:
| Option | What it does |
|---|---|
target |
{ panel, hostId }: one host to show the notification in, instead of the component's own. |
targetPanels |
panels, such as ['channel-list'], to show it in every host of. Without target and targetPanels, a target tag already in payload.options.tags is kept as the only one. |
incident |
{ domain, entity, operation, status? }, used to build type when the payload has none. status defaults to failed for an error, success for a success, else the severity. |
addSystemNotification takes only incident.
import { Pressable, Text } from "react-native";
import { useNotificationApi } from "stream-chat-react-native";
const exportIncident = { domain: "api", entity: "chat", operation: "export" };
export const ExportButton = ({
exportChat,
}: {
exportChat: () => Promise<void>;
}) => {
const { addNotification } = useNotificationApi();
const onPress = async () => {
try {
await exportChat();
// Published as api:chat:export:success
addNotification(
{
message: "Chat exported",
options: { severity: "success" },
origin: { emitter: "ExportButton" },
},
{ incident: exportIncident },
);
} catch (error) {
// Published as api:chat:export:failed
addNotification(
{
message: "The chat could not be exported",
options: {
originalError: error instanceof Error ? error : undefined,
severity: "error",
},
origin: { emitter: "ExportButton" },
},
{ incident: exportIncident },
);
}
};
return (
<Pressable accessibilityRole="button" onPress={onPress}>
<Text>Export</Text>
</Pressable>
);
};Outside Chat, or from code that isn't a component, publish with client.notifications directly, as Publishing a notification shows.
useNotificationListController
Does what NotificationList does, and leaves the rendering to you.
useNotificationListController(params?: {
filter?: (notification: Notification) => boolean;
hostId?: string;
panel?: NotificationTargetPanel;
}): {
dismissNotification: () => void;
notification: Notification | null;
}The target it serves is { panel, hostId } when you pass both. With neither, it's the target of the nearest NotificationTargetProvider. With only panel, it's that provider's target when its panel matches, otherwise none.
- Returns:
notification, the one to show, ornull.dismissNotification()removes it. - Reads:
client.notifications.store, throughuseNotifications({ target, filter, requireTarget: true }). System notifications are left out. - Re-renders: whenever a notification is added, removed or changed.
- Needs:
Chat. Throws outside it. Without a target it returnsnull.
While it's mounted it does the list's work:
- It picks the notification to show. A notification without a duration goes first; otherwise the newest one shows.
- It removes every other notification for its target from the store.
- It starts the countdown of the notification it shows, at least 5 seconds for one with actions.
- It registers its target as the latest mounted one, so a notification published without a target goes to it while it's the latest.
- When it unmounts, it removes every notification for its target.
import { Pressable, Text, View } from "react-native";
import { useNotificationListController } from "stream-chat-react-native";
export const TopBanner = () => {
const { dismissNotification, notification } = useNotificationListController();
if (!notification) return null;
return (
<View accessibilityLiveRegion="polite">
<Text>{notification.message}</Text>
<Pressable accessibilityRole="button" onPress={dismissNotification}>
<Text>Close</Text>
</Pressable>
</View>
);
};Render it inside a target, such as a Channel. Next to the default NotificationList of the same target, both pick the same notification. Replace the list through WithComponents instead, as Replacing the list or a notification shows.
useNotificationTarget
Returns the kind of surface the component sits in.
useNotificationTarget(): NotificationTargetPanel | undefined- Returns, the first that applies:
'thread'inside aThread;'channel'inside aChannel;'thread-list'inside aThreadList;'channel-list'inside aChannelList;undefined.
- Reads: the
Thread,Channel,ThreadListandChannelListcontexts. No store. - Re-renders: when one of those contexts changes.
- Needs: nothing. Never throws.
It returns only the panel, never a host ID, and it doesn't know about channel details screens. For the exact target, use useNotificationTargetContext().
useNotificationTargetContext
Returns the target of the nearest NotificationTargetProvider.
useNotificationTargetContext(): NotificationTarget | undefined- Returns:
{ panel, hostId }, orundefinedoutside every provider. - Reads: the context of
NotificationTargetProvider. No store. - Re-renders: when the provider's
panelorhostIdchanges. - Needs: nothing. Never throws.
useResolvedNotificationTarget({ hostId?, panel? }) resolves a target the way useNotificationListController does, from the params or this context.
To give a part of your app a list of its own, wrap it in a provider and render a list inside it:
import type { PropsWithChildren } from "react";
import { View } from "react-native";
import {
NotificationList,
NotificationTargetProvider,
} from "stream-chat-react-native";
export const SettingsArea = ({ children }: PropsWithChildren) => (
<NotificationTargetProvider hostId="settings" panel="settings">
<View style={{ flex: 1 }}>
{children}
<NotificationList />
</View>
</NotificationTargetProvider>
);useClientNotifications
Returns every notification in the store.
useClientNotifications(): { notifications: Notification[] }- Returns:
{ notifications }, the whole list, system notifications included. - Reads:
client.notifications.store, fieldnotifications. - Re-renders: when the list changes.
- Needs:
Chat. Throws outside it.
useNotifications() without options returns the same list.