# Publishing notifications

`client.notifications` is the client's notification manager, the service through which notifications are published and managed. A notification is an object that describes what just happened, such as a failed upload. It carries the text to show, such as "Error uploading attachment", its severity, who published it, and a `type` that identifies what it's about. A UI uses the `type` to show a translated text instead. The SDK publishes one when an upload, a send or another operation goes wrong, and your code can publish its own. The manager keeps the current notifications in its store, and a UI reads them from there and shows each one for a while, or until the user closes it. Use this page to publish notifications of your own, to translate or filter the SDK's by type, and to control how long each one stays.

>
> **Info:** **Example**
>
> The user adds a 300 MB video to a message, but the Stream app accepts files of up to 100 MB:
>
> 1. The composer's upload middleware refuses the file and calls `client.notifications.addError()`.
> 2. The notification goes into `client.notifications.store`, with the severity `error`, the type `validation:attachment:upload:blocked` and the reason `size_limit` in its metadata.
> 3. The SDK's `NotificationList` shows it, and starts its countdown with `client.notifications.startTimeout(id)`. After three seconds it's removed.
>

How notifications are shown, where each one appears, and the ones the SDK's components publish, are on [Notifications UI](https://getstream.io/chat/docs/sdk/react-native/ui/components/notifications-ui/).

## Publishing a notification

`client.notifications.add()` publishes a notification and returns its id. It takes the `message` to show, an `origin` that says who published it, and `options`:

```ts
const id = client.notifications.add({
  message: "Settings saved",
  origin: { emitter: "SettingsScreen" },
  options: { severity: "success", type: "app:settings:save:success" },
});
```

`client.notifications.addError()`, `client.notifications.addWarning()`, `client.notifications.addInfo()` and `client.notifications.addSuccess()` do the same with the severity set:

```ts
const saveSettings = async (save: () => Promise<void>) => {
  try {
    await save();
    client.notifications.addSuccess({
      message: "Settings saved",
      origin: { emitter: "SettingsScreen" },
    });
  } catch (error) {
    client.notifications.addError({
      message: "Could not save the settings",
      origin: { context: { section: "profile" }, emitter: "SettingsScreen" },
      options: {
        originalError: error instanceof Error ? error : undefined,
        type: "app:settings:save:failed",
      },
    });
  }
};
```

What a notification carries:

| Field                   | What it holds                                                                                       |
| ----------------------- | --------------------------------------------------------------------------------------------------- |
| `message`               | The text. The SDK's own messages are plain English for developers; a UI should translate by `type`. |
| `origin.emitter`        | The publisher, such as `"AttachmentManager"`, so a UI can pick out some publishers.                 |
| `origin.context`        | What a UI needs to know, such as the attachment. Optional.                                          |
| `options.severity`      | `'error'`, `'warning'`, `'info'`, `'success'`, or a string of your own.                             |
| `options.type`          | What the notification is about. See [Notification types](#notification-types).                      |
| `options.actions`       | The data for the action buttons a UI shows with the notification, each `{ label, handler }`.        |
| `options.duration`      | How long to show it, in milliseconds.                                                               |
| `options.metadata`      | Extra data, such as the reason for a failure.                                                       |
| `options.tags`          | Labels a UI can use to decide where to show it.                                                     |
| `options.originalError` | The error that caused it.                                                                           |

The SDK shows notifications per screen area, such as the channel list, a channel or a thread, and reads where each one belongs from its tags. In a component, `useNotificationApi().addNotification()` publishes to the same list and tags the notification for the area the component sits in. [Notifications UI](https://getstream.io/chat/docs/sdk/react-native/ui/components/notifications-ui/) explains the targets.

## Notification types

A notification's `type` says what it's about, so a UI can translate it or group it. The types `stream-chat` publishes follow the pattern `domain:entity:operation:result`:

- `domain`: where it happened, such as `api` for a request or `validation` for a check before one.
- `entity`: what it's about, such as `attachment`, `poll` or `message`.
- `operation`: what was attempted, such as `upload`, `create` or `castVote`.
- `result`: how it went, such as `failed`, `blocked`, `limit` or `success`.

So `api:attachment:upload:failed` is an upload request that failed. Every type `stream-chat` publishes is in `CORE_NOTIFICATION_TYPE`, and the values don't change between minor releases. For your own notifications, any string works, but the same pattern keeps them easy to tell apart. Match on `type`, never on `message`: the message is a fallback whose wording can change. The SDK translates the `stream-chat` notifications it shows by their `type`, as [Localization](https://getstream.io/chat/docs/sdk/react-native/features/app-experience/localization/) explains.

## Severity, actions and durations

The severity decides how long a notification stays: three seconds for each of `error`, `warning`, `info` and `success` by default. Change the durations in the client's [configuration](https://getstream.io/chat/docs/sdk/react-native/state/state-and-entities/configuration/). Naming one severity keeps the others:

```ts
client.config.set({
  client: { notifications: { durations: { error: 8000 } } },
});
```

A notification disappears on its own after its duration, which is `options.duration` if it has one, or else the configured duration for its severity. It stays until it's removed when:

- its duration is `0`;
- it has no duration and no severity;
- its severity is one of your own, such as `'critical'`, with no duration configured for it.

```ts
import { Linking } from "react-native";
import { StreamChat } from "stream-chat";

const client = StreamChat.getInstance("YOUR_API_KEY");

client.notifications.addWarning({
  message: "You're offline. Messages will be sent when you reconnect.",
  origin: { emitter: "ConnectionBanner" },
  options: {
    actions: [
      { handler: () => void Linking.openSettings(), label: "Open settings" },
    ],
    duration: 0,
  },
});
```

A notification can also offer actions, which are things the user can do about it, such as Open settings in the example above. Each action in `options.actions` is `{ label, handler }`. The manager only stores them with the notification. Your UI shows each one as a button with its `label`, and calls its `handler` when the user presses it:

```tsx
import { Pressable, Text, View } from "react-native";
import type { Notification } from "stream-chat";
import { useChatContext } from "stream-chat-react-native";

export const NotificationActions = ({
  notification,
}: {
  notification: Notification;
}) => {
  const { client } = useChatContext();

  return (
    <View style={{ flexDirection: "row" }}>
      {(notification.actions ?? []).map(({ handler, label }) => (
        <Pressable
          accessibilityRole="button"
          key={label}
          onPress={() => {
            handler();
            // The user acted on the notification, so it can go.
            client.notifications.remove(notification.id);
          }}
        >
          <Text>{label}</Text>
        </Pressable>
      ))}
    </View>
  );
};
```

## Following and managing notifications

The countdown doesn't start on its own. The UI starts it when it shows the notification, so a notification nobody has seen yet isn't removed.

| To                                                                              | Call                                      | What happens                                                                                                                                                                      |
| ------------------------------------------------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| follow the list                                                                 | subscribe to `client.notifications.store` | It holds the list as `{ notifications }`, like any [store](https://getstream.io/chat/docs/sdk/react-native/state/state-and-entities/reactive-state/). `useNotifications()` returns it in a component. |
| start the countdown when the notification is shown                              | `client.notifications.startTimeout(id)`   | The notification is removed once its duration has passed.                                                                                                                         |
| start the countdown only if it isn't running yet                                | `client.notifications.ensureTimeout(id)`  | Same as `startTimeout()`, but a running countdown isn't restarted.                                                                                                                |
| pause the countdown, for example while the user interacts with the notification | `client.notifications.clearTimeout(id)`   | The countdown stops. Start it again to resume.                                                                                                                                    |
| remove one notification                                                         | `client.notifications.remove(id)`         | It leaves the list.                                                                                                                                                               |
| remove them all                                                                 | `client.notifications.clear()`            | The list is empty.                                                                                                                                                                |

## Who publishes notifications

`stream-chat` and the SDK's components publish notifications of their own, and your code publishes with the same calls. [Notifications](https://getstream.io/chat/docs/sdk/react-native/reference/notifications/) lists every one they publish, with its type, severity, `origin.emitter` and when it's published. The message list's emitters carry the method name, such as `MessagePaginator.jumpToMessage`.

With offline support, a send or an edit that fails because the device is offline publishes `api:message:send:failed` or `api:message:update:failed` too, although it waits as a pending task and is sent after the next reconnect.

## Pitfalls

- **Matching on the message.** The SDK's messages are English fallbacks and can be reworded. Use `type`.
- **A notification that never goes away.** The manager removes a notification only when its countdown ends, and the countdown starts only when `client.notifications.startTimeout(id)` is called. The SDK's notification components call it when they show a notification. A UI of your own has to call it too, or the notification stays in the list.
- **Durations set in the wrong place.** In `client.config.set()`, the durations go under `client.notifications.durations`, as in `{ client: { notifications: { durations: { error: 8000 } } } }`. A top-level `notifications` key isn't a setting, so the durations stay at their defaults.

## Used in

- [Notifications text](https://getstream.io/chat/docs/sdk/react-native/features/notifications/notifications-text/)
- [Sending with pending uploads](https://getstream.io/chat/docs/sdk/react-native/features/messaging/sending-with-pending-uploads/)
- [Connection status](https://getstream.io/chat/docs/sdk/react-native/features/app-experience/connection-status/)

---

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