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

Notification hooks

These hooks read the notifications in client.notifications and publish new ones from a component. They're exported from stream-chat-react.

What a notification holds, and who publishes which, is on Publishing notifications. How NotificationList shows them, and how a notification reaches a panel, is on Displaying notifications.

Each entry gives the signature, then:

  • Returns: what the hook gives back.
  • Reads: the store or the context it reads.
  • Re-renders: what makes the component render again.
  • Needs: where the component must sit, and what the hook does outside that place.

useNotifications

Returns the notifications in client.notifications, narrowed by the options.

useNotifications(options?: UseNotificationsOptions): Notification[]

The options, each optional:

  • panel: keep only the notifications routed to this panel, 'channel', 'thread', 'channel-list', 'thread-list' or 'modal'. Routing notifications explains the routing.

  • fallbackPanel: the panel a notification without a panel tag belongs to. 'channel' by default.

  • applyDisplayFilter: also apply the display filter of Chat or of the nearest NotificationConfigurationProvider, as NotificationList does.

  • filter: (notification) => boolean, applied last.

  • Returns: the notifications that pass, in the order of the store. Without options, every notification, system notifications included. System notifications carry no panel tag, so they also pass for the fallbackPanel, 'channel' unless you set it: drop them with hasSystemNotificationTag.

  • Reads: client.notifications.store, field notifications.

  • Re-renders: whenever a notification is added or removed, even one the options drop. A new filter function subscribes again, so define it at module scope, as in Selector identity.

  • Needs: Chat. Throws outside it.

A notification you render this way leaves only when its countdown runs. Start it with ensureNotificationTimeout(id) from useNotificationApi once it's on screen:

import { useEffect } from "react";
import type { Notification } from "stream-chat";
import {
  hasSystemNotificationTag,
  useNotificationApi,
  useNotifications,
} from "stream-chat-react";

const isPanelNotification = (notification: Notification) =>
  !hasSystemNotificationTag(notification);

export const ChannelToasts = () => {
  const notifications = useNotifications({
    filter: isPanelNotification,
    panel: "channel",
  });
  const { ensureNotificationTimeout, removeNotification } =
    useNotificationApi();

  useEffect(() => {
    notifications.forEach(({ id }) => ensureNotificationTimeout(id));
  }, [ensureNotificationTimeout, notifications]);

  return (
    <ul className="toasts">
      {notifications.map(({ id, message }) => (
        <li key={id}>
          {message}
          <button onClick={() => removeNotification(id)} type="button">
            Close
          </button>
        </li>
      ))}
    </ul>
  );
};

useSystemNotifications

Returns the system notifications: the ones about the whole app, such as "Reconnecting…".

useSystemNotifications(options?: UseSystemNotificationsOptions): Notification[]
  • Returns: the notifications tagged system, narrowed by options.filter when you pass one. Chat publishes the lost-connection notification this way, and no NotificationList shows it.
  • Reads: client.notifications.store, field notifications.
  • Re-renders: whenever a notification is added or removed. Define filter at module scope, as for useNotifications.
  • Needs: Chat. Throws outside it.

System notifications renders a connection banner with it, and the System notification banner recipe builds a complete one.

useNotificationApi

Returns the functions a component publishes and removes notifications with.

useNotificationApi(): NotificationApi
  • Returns:
    • addNotification(params): publishes a notification tagged for the panel the component sits in, the one useNotificationTarget returns. While a modal is open, it also tags it for the modal panel. Returns nothing.
    • addSystemNotification(params): publishes a notification tagged system and for no panel. Returns its id.
    • removeNotification(id): removes a notification.
    • startNotificationTimeout(id): starts a notification's countdown, from the beginning if it's already running.
    • ensureNotificationTimeout(id): starts the countdown unless it's already running.
  • Reads: the panel from useNotificationTarget(), and whether a modal is open from the modal dialog manager.
  • Re-renders: when the component's panel changes, and when a modal opens or closes.
  • Needs: Chat. Throws outside it.

addNotification takes:

Param What it does
message the text to show. Required.
emitter who published it, such as the component's name. Required.
severity 'error', 'warning', 'info', 'success', or a string of your own such as 'loading'. The first four have a default duration.
type what the notification is about, such as 'api:message:pin:success'. Translators and filters match on it.
incident { domain, entity, operation, status? }, used to build type when you don't pass one. status defaults to failed for an error, success for a success, else the severity.
duration milliseconds before it leaves, counted from when it's shown.
actions buttons, each { label, handler }.
context data stored as origin.context, such as the message it's about.
error the error that caused it, stored as originalError.
tags tags added next to the panel tags.
targetPanels the panels to show it in, instead of the component's own.
ariaLive 'polite' or 'assertive': how urgently screen readers announce it. By default assertive for an error, polite otherwise.

addSystemNotification takes the same, without targetPanels.

import { useNotificationApi } from "stream-chat-react";

const exportIncident = { domain: "api", entity: "chat", operation: "export" };

export const ExportButton = ({
  exportChat,
}: {
  exportChat: () => Promise<void>;
}) => {
  const { addNotification } = useNotificationApi();

  const onClick = async () => {
    try {
      await exportChat();
      // Published as api:chat:export:success
      addNotification({
        emitter: "ExportButton",
        incident: exportIncident,
        message: "Chat exported",
        severity: "success",
      });
    } catch (error) {
      // Published as api:chat:export:failed
      addNotification({
        emitter: "ExportButton",
        error: error instanceof Error ? error : undefined,
        incident: exportIncident,
        message: "The chat could not be exported",
        severity: "error",
      });
    }
  };

  return (
    <button onClick={onClick} type="button">
      Export
    </button>
  );
};

Outside Chat, or from code that isn't a component, publish with client.notifications directly, as Publishing a notification shows. Those notifications carry no panel tag.

useNotificationTarget

Returns the panel the component sits in.

useNotificationTarget(): NotificationTargetPanel | undefined
  • Returns, the first that applies:
    1. 'thread' inside a Thread;
    2. 'channel' inside a Channel;
    3. 'thread-list' while the layout shows the threads view;
    4. 'channel-list' inside ChannelLists;
    5. undefined.
  • Reads: the Thread, Channel, workspace navigation and channel list contexts. No store.
  • Re-renders: when one of those contexts changes.
  • Needs: nothing. Never throws.

MessageList uses it to pick the panel of its notification list. A list of your own can do the same:

import { NotificationList, useNotificationTarget } from "stream-chat-react";

export const PanelNotifications = () => {
  const panel = useNotificationTarget();
  return <NotificationList panel={panel ?? "channel"} />;
};