# Publishing notifications

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

`client.notifications` is the client's list of notifications: short messages about what just happened, such as "Error uploading attachment" or "Settings saved". The SDK publishes to it when an upload, a send or another operation goes wrong, and your code can publish to it too. A UI reads the list and shows each notification for a while, or until the user closes it.

>
> **Info:** **Example**
>
> The user adds a 30 MB video to a message, but the app only accepts 20 MB files:
>
> 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 UI shows it, and starts its countdown with `client.notifications.startTimeout(id)`. After three seconds it's removed.
>

Displaying notifications, and the ones the React components publish, are on [Displaying notifications](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/displaying-notifications/).

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

- `message`: the text. The SDK's own messages are plain English for developers; a UI should translate by `type`.
- `origin`: `{ emitter, context? }`. The emitter names the publisher, such as `"AttachmentManager"`, so a UI can pick out some publishers. `context` holds what a UI needs to know, such as the attachment.
- `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`: buttons, 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.

`client.notifications.remove(id)` removes one notification, and `client.notifications.clear()` removes them all.

`client.notifications.store` holds the list as `{ notifications }`. Subscribe to it to follow the list outside a UI component, as with any [store](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/reactive-state/).

## 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/v15/state/state-and-entities/configuration/). Naming one severity keeps the others:

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

`options.duration` overrides the severity's duration for one notification. A notification stays until it's removed when its duration is `0`, or when it has no duration and no severity:

```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,
  },
});
```

The countdown doesn't start on its own. It starts with `client.notifications.startTimeout(id)`, which the UI calls when it shows the notification, so a notification nobody has seen yet isn't removed. `client.notifications.ensureTimeout(id)` starts it only if it isn't running yet, and `client.notifications.clearTimeout(id)` stops it, for example while the pointer rests on the notification.

The manager only keeps the actions. Showing them as buttons and calling `handler` is up to the UI.

## Who publishes notifications

These are the notifications `stream-chat` publishes:

| Type                                       | Severity | Published by        | When                                                                                   |
| ------------------------------------------ | -------- | ------------------- | -------------------------------------------------------------------------------------- |
| `api:attachment:upload:failed`             | error    | `AttachmentManager` | An upload request failed. Not for an upload the user cancelled.                        |
| `validation:attachment:upload:blocked`     | error    | `AttachmentManager` | The app's upload settings refused a file. `metadata.reason` says which setting.        |
| `validation:attachment:file:missing`       | error    | `AttachmentManager` | An attachment to upload had no file.                                                   |
| `validation:attachment:id:missing`         | error    | `AttachmentManager` | An attachment to upload had no local id.                                               |
| `validation:attachment:upload:in-progress` | warning  | `MessageComposer`   | A message was sent while an upload was running, with pending uploads off.              |
| `validation:command:disabled`              | warning  | `MessageComposer`   | A command was used while editing a message, or a moderation command while quoting one. |
| `validation:command:not-ready`             | warning  | `MessageComposer`   | A command was sent without what it needs, such as `/ban` without a user.               |
| `api:message:send:failed`                  | error    | `MessageComposer`   | `send()` failed. The message stays in the list, marked `failed`.                       |
| `api:message:update:failed`                | error    | `MessageComposer`   | `update()` failed. The edit stays on the message, marked `failed`.                     |
| `api:poll:create:failed`                   | error    | `MessageComposer`   | `createPoll()` failed.                                                                 |
| `api:location:create:failed`               | error    | `MessageComposer`   | `sendLocation()` failed.                                                               |
| `api:message:jump:failed`                  | error    | `MessagePaginator`  | Jumping to a message failed.                                                           |
| `api:message:jumpToLatest:failed`          | error    | `MessagePaginator`  | Jumping to the latest message failed.                                                  |
| `validation:poll:castVote:limit`           | info     | `Poll`              | A vote was cast in a poll where the user has used all their votes.                     |

The message list's emitters carry the method name, such as `MessagePaginator.jumpToMessage`.

Your own code publishes with the same calls. The React components publish more, for example when a message action succeeds; those are listed on [Displaying notifications](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/displaying-notifications/).

## Pitfalls

- **Matching on the message.** The SDK's messages are English fallbacks and can be reworded. Use `type`.
- **A notification that never goes away.** Its countdown waits for `client.notifications.startTimeout()`. A UI of your own has to call it, or `client.notifications.ensureTimeout()`, when it shows the notification.
- **The configuration key.** The durations live under `client.notifications` in `client.config.set()`, not under a top-level `notifications` key.

## Used in

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

---

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