# Notification hooks

> **Info:** This is release candidate documentation for **Stream Chat React Native SDK v10**. For the latest stable version, see the [latest version (v9)](https://getstream.io/chat/docs/sdk/react-native/).

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](https://getstream.io/chat/docs/sdk/react-native/v10/state/publishing-notifications/). How `NotificationList` shows them, and how a notification reaches a list through its target, is on [Displaying notifications](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/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.

```ts
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 a `NotificationList` serving 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 an `origin.context.panel` set to its panel. Without any of these, a composer in its `origin.context` that belongs to the host also makes it the host's. [Routing notifications](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/displaying-notifications/#routing-notifications) explains the tags.
- `requireTarget`: return `[]` while `target` is `undefined`, 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 with `hasSystemNotificationTag` in your `filter`. With a `target`, system notifications drop out, because they carry no target tag.
- **Reads:** `client.notifications.store`, field `notifications`.
- **Re-renders:** whenever a notification is added, removed or changed, even one the options drop. A new `filter` function or a new `target` object subscribes again, so define the filter at module scope and keep the target's identity, as in [Selector identity](https://getstream.io/chat/docs/sdk/react-native/v10/ui/reading-state-in-components/#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`](#usenotificationapi) once it's on screen, or use [`useNotificationListController`](#usenotificationlistcontroller), which does that for you.

## useSystemNotifications

Returns the system notifications: the ones about the whole app, not one part of it.

```ts
useSystemNotifications(options?: {
  filter?: (notification: Notification) => boolean;
}): Notification[]
```

- **Returns:** the notifications tagged `system`, narrowed by `options.filter` when you pass one. No `NotificationList` shows them.
- **Reads:** `client.notifications.store`, field `notifications`.
- **Re-renders:** whenever a notification is added, removed or changed. Define `filter` at module scope, as for `useNotifications`.
- **Needs:** `Chat`. Throws outside it.

The SDK publishes no system notification of its own: its connection indicators read the connection stores. [System notifications](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/displaying-notifications/#system-notifications) publishes one and renders it with this hook.

## useNotificationApi

Returns the functions a component publishes and removes notifications with.

```ts
useNotificationApi(): NotificationApi
```

- **Returns:**
  - `addNotification(payload, options?)`: publishes a notification tagged for the target the component sits in, the target of the nearest `NotificationTargetProvider`. `Channel`, `Thread`, `ChannelList`, `ThreadList` and the channel details screens each set one. Returns nothing.
  - `addSystemNotification(payload, options?)`: publishes a notification tagged `system` and 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. `durationOverride` replaces its duration. Without a duration or a `durationOverride`, there's no countdown.
  - `runWithNotificationTarget(callback, target?)`: runs `callback`, and tags the notifications published without a target while it runs for `target`, 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, `addNotification` publishes 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](https://getstream.io/chat/docs/sdk/react-native/v10/state/publishing-notifications/#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`.

```tsx
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](https://getstream.io/chat/docs/sdk/react-native/v10/state/publishing-notifications/#publishing-a-notification) shows.

## useNotificationListController

Does what `NotificationList` does, and leaves the rendering to you.

```ts
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, or `null`. `dismissNotification()` removes it.
- **Reads:** `client.notifications.store`, through `useNotifications({ 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 returns `null`.

While it's mounted it does the list's work:

1. It picks the notification to show. A notification without a duration goes first; otherwise the newest one shows.
2. It removes every other notification for its target from the store.
3. It starts the countdown of the notification it shows, at least 5 seconds for one with actions.
4. It registers its target as the latest mounted one, so a notification published without a target goes to it while it's the latest.
5. When it unmounts, it removes every notification for its target.

```tsx
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](https://getstream.io/chat/docs/sdk/react-native/v10/ui/customizing/displaying-notifications/#replacing-the-list-or-a-notification) shows.

## useNotificationTarget

Returns the kind of surface the component sits in.

```ts
useNotificationTarget(): NotificationTargetPanel | undefined
```

- **Returns**, the first that applies:
  1. `'thread'` inside a `Thread`;
  2. `'channel'` inside a `Channel`;
  3. `'thread-list'` inside a `ThreadList`;
  4. `'channel-list'` inside a `ChannelList`;
  5. `undefined`.
- **Reads:** the `Thread`, `Channel`, `ThreadList` and `ChannelList` contexts. 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`.

```ts
useNotificationTargetContext(): NotificationTarget | undefined
```

- **Returns:** `{ panel, hostId }`, or `undefined` outside every provider.
- **Reads:** the context of `NotificationTargetProvider`. No store.
- **Re-renders:** when the provider's `panel` or `hostId` changes.
- **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:

```tsx
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.

```ts
useClientNotifications(): { notifications: Notification[] }
```

- **Returns:** `{ notifications }`, the whole list, system notifications included.
- **Reads:** `client.notifications.store`, field `notifications`.
- **Re-renders:** when the list changes.
- **Needs:** `Chat`. Throws outside it.

`useNotifications()` without options returns the same list.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/notification-hooks/](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/notification-hooks/).