Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

Publishing notifications

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.

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:

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:

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

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. Naming one severity keeps the others:

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:

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.

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