Message Reminders

Message reminders let users set follow-up notifications for specific messages. The React SDK handles the UI and state, while the low-level JS client handles the underlying CRUD operations. For API details, see the low-level client guide. The low-level client guide

Best Practices

  • Keep reminder actions in the message menu for discoverability.
  • Use useMessageReminder state for live countdowns instead of custom timers.
  • Keep reminder offsets small and meaningful for your product.
  • Avoid frequent UI refreshes beyond the default timer boundaries.
  • Provide clear feedback when reminders are created or removed.

Message Reminder UI Interaction

Users can create, update, or delete reminders from the message actions menu. When a reminder exists, the Message UI component shows a reminder notification; removing the reminder removes the notification.

Get Message Reminder Data

The React SDK uses client.reminders to perform CRUD operations. The controller maintains reactive state: a map of message IDs to Reminder instances. Each Reminder has its own reactive state that extends the server ReminderResponse with timeLeftMs.

export type ReminderState = {
  channel_cid: string;
  created_at: Date;
  message: MessageResponse | null;
  message_id: string;
  remind_at: Date | null;
  timeLeftMs: number | null;
  updated_at: Date;
  user: UserResponse | null;
  user_id: string;
};

You can subscribe at two levels:

  1. Subscribe to a specific reminder’s state
import { useMessageReminder, useStateStore } from "stream-chat-react";
import type { LocalMessage, ReminderState } from "stream-chat";

const reminderStateSelector = (state: ReminderState) => ({
  timeLeftMs: state.timeLeftMs,
});

const Component = ({ message }: { message: LocalMessage }) => {
  // access the message reminder instance
  const reminder = useMessageReminder(message.id);
  const { timeLeftMs } =
    useStateStore(reminder?.state, reminderStateSelector) ?? {};
};

timeLeftMs updates periodically. The closer the deadline, the more frequent the updates (daily → hourly → minutely), and the reverse as you move away from the deadline.

  1. Subscribe to reminders pagination

Pagination is handled by RemindersPaginator. The items are ReminderResponse objects from the server, not Reminder instances.

import {
  InfiniteScrollPaginator,
  useChatContext,
  useStateStore,
} from "stream-chat-react";
import type { PaginatorState, ReminderResponse } from "stream-chat";
import { CustomLoadingIndicator } from "../LoadingIndicator";
import { CustomReminderListingItem } from "./CustomReminderListingItem";

const reminderPaginatorStateSelector = (
  state: PaginatorState<ReminderResponse>,
) => ({
  isLoading: state.isLoading,
  items: state.items,
});

const Component = () => {
  const { client } = useChatContext();
  const { items, isLoading } = useStateStore(
    client.reminders.paginator.state,
    reminderPaginatorStateSelector,
  );

  return (
    <InfiniteScrollPaginator
      loadNextOnScrollToBottom={client.reminders.queryNextReminders}
      threshold={40}
    >
      {items && (
        <div>
          {items.map((item) => (
            <CustomReminderListingItem reminder={item} key={item.message_id} />
          ))}
        </div>
      )}
      {isLoading && <CustomLoadingIndicator />}
    </InfiniteScrollPaginator>
  );
};

Message Reminder Configuration

You can configure the reminder offsets users can pick from:

const minute = 60 * 1000;
client.reminders.updateConfig({
  scheduledOffsetsMs: [30 * minute, 60 * minute],
});

You can also set the boundary for when the reminder notification stops refreshing elapsed time:

const day = 24 * 60 * 60 * 1000;
client.reminders.updateConfig({
  stopTimerRefreshBoundaryMs: day,
});

Overriding Default Message Reminder Components

You can override the reminder components in the React SDK:

import {
  Channel,
  ReminderNotificationProps,
  useMessageReminder,
  useStateStore,
} from 'stream-chat-react';
import type { LocalMessage, ReminderState } from 'stream-chat';

const reminderStateSelector = (state: ReminderState) => ({
  timeLeftMs: state.timeLeftMs,
});

const CustomReminderNotification = ({ reminder }: ReminderNotificationProps) => {
  const { timeLeftMs } =
    useStateStore(reminder?.state, reminderStateSelector) ?? {};

  return (
    // ... the reminder notification UI component
  );
};

const Component = () => {
  return (
    <Channel ReminderNotification={CustomReminderNotification}>
      {/* ... */}
    </Channel>
  );
};