# 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 30 MB video to a message, but the app only accepts 20 MB files:
>
> 1. The composer refuses the file and publishes an error notification about it, with the reason: the file is too large.
> 2. The UI shows the notification and starts its countdown.
> 3. After three seconds, the notification is removed.
>

How notifications are shown is on [Notifications UI](https://getstream.io/chat/docs/sdk/react/ui/components/notifications-ui/), and the ones the React components publish are listed on [Notifications](https://getstream.io/chat/docs/sdk/react/reference/notifications/#stream-chat-react).

## 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: "SettingsPanel" },
  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: "SettingsPanel" },
    });
  } catch (error) {
    client.notifications.addError({
      message: "Could not save the settings",
      origin: { context: { section: "profile" }, emitter: "SettingsPanel" },
      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`       | Data necessary for actions buttons displayed with the notification UI. 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.                                                                           |

## 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.

## 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/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: `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
client.notifications.addWarning({
  message: "You're offline. Messages will be sent when you reconnect.",
  origin: { emitter: "ConnectionBanner" },
  options: {
    actions: [{ handler: () => window.location.reload(), label: "Reload" }],
    duration: 0,
  },
});
```

A notification can also offer actions: things the user can do about it, such as Reload 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.

```ts
import type { Notification } from "stream-chat";

// Shows a notification's actions as buttons in an element of your own.
const showActions = (notification: Notification, container: HTMLElement) => {
  const buttons = (notification.actions ?? []).map(({ handler, label }) => {
    const button = document.createElement("button");
    button.textContent = label;
    button.addEventListener("click", () => {
      handler();
      // The user acted on the notification, so it can go.
      client.notifications.remove(notification.id);
    });
    return button;
  });

  container.replaceChildren(...buttons);
};
```

## 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/state/state-and-entities/reactive-state/). |
| 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 pointer rests on 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

Both `stream-chat` and `stream-chat-react` publish notifications of their own, and your code publishes with the same calls. [Notifications](https://getstream.io/chat/docs/sdk/react/reference/notifications/) lists every one they publish, with its type, severity, `origin.emitter` and when it's published.

## 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 UI](https://getstream.io/chat/docs/sdk/react/ui/components/notifications-ui/)
- [Sending with pending uploads](https://getstream.io/chat/docs/sdk/react/features/messaging/sending-with-pending-uploads/)
- [Connection status](https://getstream.io/chat/docs/sdk/react/features/app-experience/connection-status/)

---

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