# In-App Notifications

Stream Chat ships an SDK-level notification system that surfaces transient feedback, including errors, confirmations, warnings, and info, inside your app without alerts, modals, or push notifications. The SDK already emits notifications for built-in actions like deleting a message, pinning a channel, sending a poll vote, or uploading an attachment. You can also emit your own notifications from any component, and customize how they're rendered.

The notification UI is the **snackbar**, a single floating card shown above the message list, channel list, or thread. It animates in, dismisses after a timeout (or stays until the user dismisses it), and supports action buttons.

## Best Practices

- Mount [`NotificationList`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/notification-list/) once per active target (channel, thread, channel list, thread list) rather than once at the app root, so notifications appear in the context where the action happened.
- Let the SDK render notifications by default. Override the [`Notification`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/notification/) component or replace [`NotificationList`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/notification-list/) with `WithComponents` only when theme tokens are not enough.
- Use `incident` instead of hand-rolling the `type` string. The SDK derives `domain:entity:operation:status` from the incident, which keeps analytics and dedupe consistent across the codebase.
- Prefer `severity` (`'error' | 'success' | 'warning' | 'info'`) over custom severities. Known severities get default durations and live-region semantics; `error`, `warning`, and `success` also get built-in icons.
- Use `duration: 0` sparingly. Notifications without a duration are persistent too. Persistent notifications stay on screen until dismissed and the snackbar shows only one notification at a time, so they block any newer transient notification from appearing.
- For app-level notifications that should never appear in the snackbar (analytics, background errors, dev warnings), call `addSystemNotification` and read them via [`useSystemNotifications`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/hooks/use-system-notifications/).

## How It Works

The notification system has three layers:

1. **`stream-chat` `NotificationManager`** owns the notification store. Every notification is just an object with `id`, `message`, `origin`, `severity`, `type`, `tags`, etc. The manager is exposed as `client.notifications` and uses a state-store pattern so subscribers re-render when notifications are added, removed, or updated.
2. **SDK hooks**: [`useNotificationApi`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/hooks/use-notification-api/) wraps the manager for emitting, [`useNotifications`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/hooks/use-notifications/) reads filtered notifications for a target, and [`useNotificationTarget`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/hooks/use-notification-target/) infers which panel (`channel`, `thread`, `channel-list`, `thread-list`) the calling component lives in.
3. **`NotificationList`** is the rendered snackbar host. It picks the highest-priority notification for its target, calls `Notification` to render it, starts the dismiss timer, and removes the notification when the user swipes or taps the close button.

```text
client.notifications (stream-chat)
        │
        ▼
useNotificationApi / useNotifications     ← emit / read
        │
        ▼
NotificationList                          ← picks & renders
        │
        ▼
Notification + NotificationIcon           ← snackbar UI
```

When you call `addNotification` from a component, the SDK tags the notification with the nearest notification target so it appears only in the matching `NotificationList`. If multiple `NotificationList` instances are mounted (one per channel, one for the thread, one for the channel list, one for the thread list), each receives only the notifications that belong to it.

## Severity Model

Each notification has an optional `severity`. The built-in `Notification` component maps each severity to an icon and an accessibility role:

| Severity    | Icon               | Role / live-region    |
| ----------- | ------------------ | --------------------- |
| `'error'`   | Exclamation circle | `alert` / `assertive` |
| `'warning'` | Exclamation circle | `summary` / `polite`  |
| `'success'` | Checkmark          | `summary` / `polite`  |
| `'info'`    | No icon            | `summary` / `polite`  |
| `'loading'` | Refresh (spinning) | `summary` / `polite`  |

Default auto-dismiss durations are configured on the [`NotificationManager`](https://github.com/GetStream/stream-chat-js/blob/master/src/notifications/NotificationManager.ts) (3 seconds for `error`, `warning`, `success`, and `info`). `loading` has a built-in icon but no default duration unless you configure one or pass `duration` in the notification options.

## Panel Targeting

Notifications are routed to UI panels via tags of the form `target:<panel>` (with optional `:<hostId>` suffix when targeting a specific channel or thread instance).

The SDK recognizes four built-in panels:

| Panel            | Where it shows                          |
| ---------------- | --------------------------------------- |
| `'channel'`      | Inside an active channel's message list |
| `'thread'`       | Inside the active thread view           |
| `'channel-list'` | On the channel list screen              |
| `'thread-list'`  | On the thread list screen               |

When you call `addNotification` without passing `target` or `targetPanels`, the SDK uses the nearest mounted `NotificationTargetProvider`. The built-in `Channel`, `Thread`, `ChannelList`, and `ThreadList` components install that provider for you. You can also pass `targetPanels: ['channel-list']` to target a panel explicitly, regardless of where the call originates.

For advanced flows (running a network call after the user navigates away from a channel and routing the resulting notification back to that channel) use `runWithNotificationTarget`. See the [In-App Notifications cookbook](/chat/docs/sdk/react-native/guides/in-app-notifications/#routing-notifications-from-a-background-action).

## Lifecycle

A notification goes through three stages:

1. **Created**: `addNotification(payload)` adds it to the store. `NotificationList` picks it as the active notification if its target matches.
2. **Visible**: The snackbar renders. The dismiss timer starts via `startNotificationTimeout(id)` when the notification has a duration. If the notification has `actions`, the timer is extended to at least 5 seconds so the user has time to tap.
3. **Dismissed**: Times out, the user swipes / taps close, or your code calls `removeNotification(id)`. The active target's `NotificationList` clears the snackbar.

A notification with `duration: 0` or no resolved duration skips step 3's timer and stays until dismissed manually. Persistent notifications take priority over transient ones. If both are present in the same panel, the persistent one wins.

When a `NotificationList` unmounts, it clears all non-system notifications for its target. This prevents stale notifications from a previous screen leaking into a new one.

## Built-in Notifications

The SDK emits notifications automatically for these flows:

| Flow                                      | Type prefix                                                | Where it fires                          |
| ----------------------------------------- | ---------------------------------------------------------- | --------------------------------------- |
| Message delete / pin / mute               | `api:message:*` / `api:user:*`                             | `useMessageActionHandlers`              |
| Channel pin / archive / mute / block user | `api:channel:*` / `api:user:*`                             | `useChannelActions`                     |
| Poll create / end / vote                  | `api:poll:*` / `validation:poll:*`                         | `messageComposer`, `usePollState`       |
| Reactions fetch                           | `api:message:reactions:fetch:failed`                       | `useFetchReactions`                     |
| Jump to first unread                      | `channel:jumpToFirstUnread:failed`                         | `useMessageListPagination`              |
| Audio recording                           | `permission:audio:*` / `validation:audio:*`                | `AudioRecordingButton`, `AudioRecorder` |
| Attachment upload                         | `validation:attachment:*` / `api:attachment:upload:failed` | `messageComposer/attachmentManager`     |
| Command validation                        | `validation:command:disabled`                              | `textComposer` middleware               |

These notifications are i18n-aware. Translation keys live in `package/src/i18n/<locale>.json`, for example, `Message deleted`, `Attachment upload blocked due to {{reason}}`, and `Failed to create the poll due to {{reason}}`. Translate them by overriding the keys in your `Streami18n` instance.

## System Notifications

Some notifications shouldn't show up in the snackbar, such as analytics events, dev-only warnings, or internal sync errors. Emit them with `addSystemNotification` and they're tagged with the reserved `system` tag, which excludes them from `NotificationList`. Read them with [`useSystemNotifications`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/hooks/use-system-notifications/) and forward them to your analytics pipeline, logger, or background queue.

## Migration from `useInAppNotificationsState`

The previous in-app notifications API (`useInAppNotificationsState`, `useClientNotifications`, and the recommendation to hand-roll a toaster) is deprecated for snackbar use. The new API renders the snackbar for you and uses the same store as the rest of the SDK.

| Old                                                     | New                                                                                                                                                                                                 |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `openInAppNotification(notification)`                   | `addNotification({ message, origin, options }, { incident, targetPanels })` from [`useNotificationApi`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/hooks/use-notification-api/) |
| `closeInAppNotification(id)`                            | `removeNotification(id)` from [`useNotificationApi`](/chat/docs/sdk/react-native/ui-components/in-app-notifications/hooks/use-notification-api/)                                                    |
| `notifications` array from `useInAppNotificationsState` | `useNotifications()`                                                                                                                                                                                |
| `useClientNotifications()`                              | `useNotifications()` (it now reads the same store the client writes to)                                                                                                                             |
| Custom `<Toast />` mounted at app root                  | `<NotificationList />` mounted by `MessageList`, `ChannelList`, and `ThreadList` in the built-in layouts                                                                                            |

The notification object shape is unchanged; it still comes from `stream-chat`. The only structural change is that `created_at` is now `createdAt` (camelCase).

For end-to-end customization examples, including emitting, routing, and custom rendering, see the two cookbook pages:

- [In-App Notifications cookbook](/chat/docs/sdk/react-native/guides/in-app-notifications/): emit, read, route.
- [Snackbar cookbook](/chat/docs/sdk/react-native/guides/snackbar/): theme, override, replace.


---

This page was last updated at 2026-05-14T07:09:01.585Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/guides/in-app-notifications-overview/](https://getstream.io/chat/docs/sdk/react-native/guides/in-app-notifications-overview/).