# Reminders and save for later

Reminders bring a message back to the user later. "Remind me" sets a time, and "Save for later" bookmarks the message with no time. Both are one feature in the API: a reminder with or without a `remind_at`.

## What users get

The user opens the menu on a message and picks Remind me. A submenu offers times: in 2 minutes, in 30 minutes, in an hour, in 2 hours, in 8 hours and in a day. They pick 30 minutes, and the message now shows "Reminder set · Due in 30 minutes". The time counts down as it approaches. A reminder more than 59 minutes away shows its calendar time instead, such as "Today at 15:00".

Save for later marks the message "Saved for later". The same menu then offers Remove reminder or Remove save for later. A message has one reminder at most, so while it has a scheduled reminder the menu hides Save for later, and while it's saved for later the menu hides Remind me.

Reminders belong to the user, not to the device: their reminders show on every device they're signed in on.

## How it works across the layers

- **State.** `client.reminders` is the reminder manager. It keeps one `Reminder` per message, keyed by the message id, and talks to the reminders API. Its `paginator` is the reminder list, the user's reminders across all channels.
- **UI.** `MessageUI` looks up the message's reminder with `useMessageReminder(message.id)` and renders `ReminderNotification` when there is one. The `remindMe` and `saveForLater` actions in the message menu create and delete reminders. `Chat` starts the manager's event subscriptions and timers when it mounts and stops them when it unmounts.
- **Layout.** Not involved.

`client.reminders.state` holds a `Map` from message id to `Reminder`. Channel queries, reminder and message events, the reminder list and your own calls fill it; [Events](https://getstream.io/chat/docs/sdk/react/reference/state/reminder-manager/#events) lists what changes it.

Save for later is a reminder without a `remind_at`. The Remind me submenu calls `ReminderManager.upsertReminder()`, and Save for later calls `ReminderManager.createReminder()`. Neither is optimistic: the state changes once the server answers.

```ts
// Remind me in an hour, as the Remind me submenu does
await client.reminders.upsertReminder(
  { message_id: message.id },
  { remind_at: new Date(Date.now() + 60 * 60 * 1000) },
);

// Save for later: a reminder without a time
await client.reminders.createReminder({ message_id: message.id });
```

[Methods](https://getstream.io/chat/docs/sdk/react/reference/state/reminder-manager/#methods) lists the other calls, including `ReminderManager.deleteReminder()`.

### The reminder list

`client.reminders.paginator` loads the user's reminders across channels, one paginator per client. For a "Saved for later" screen, filter to reminders without a time:

1. Set `client.reminders.paginator.filters` to `{ remind_at: { $exists: false } }`. Setting `filters` or `sort` empties the list.
2. Call `ReminderManager.queryNextReminders()` for the first page, and again for each next page.
3. Call `ReminderPaginator.reload()` to load the list from the start.

[The reminder list](https://getstream.io/chat/docs/sdk/react/reference/state/reminder-manager/#the-reminder-list) explains what its items hold and when it changes. The SDK has no reminder list component; [Your own reminder list](#your-own-reminder-list) builds one.

### Reminder state and timers

Each `Reminder` has its own `state` store with the server fields and `timeLeftMs`, which a timer refreshes more often as the deadline gets closer. [Each reminder's state](https://getstream.io/chat/docs/sdk/react/reference/state/reminder-manager/#each-reminders-state) lists the fields, and [Timers](https://getstream.io/chat/docs/sdk/react/reference/state/reminder-manager/#timers) the refresh schedule. When a reminder comes due, the SDK doesn't alert the user; [Alerting when a reminder is due](#alerting-when-a-reminder-is-due) adds an alert.

## Turning it on or off

Reminders are on when both of these allow them:

- the channel type's `user_message_reminders` setting on the server, see [Message reminders](https://getstream.io/chat/docs/javascript/message-reminders/);
- `channel.userMessageReminders.enabled` in the client configuration, which defaults to `true`.

A server "no" wins. The resolved value is `channel.config.userMessageReminders.enabled`, and the message menu shows Remind me and Save for later only when it's `true`.

To turn reminders off in the client for every channel:

```ts
client.config.set({ channel: { userMessageReminders: { enabled: false } } });
```

[Configuration](https://getstream.io/chat/docs/sdk/react/state/state-and-entities/configuration/) explains `client.config` and how it combines with the server settings.

## Customizing

### The offered times

The Remind me submenu offers `scheduledOffsetsMs`, offsets from now in milliseconds:

```ts
const minute = 60 * 1000;

client.config.set({
  client: {
    reminders: {
      scheduledOffsetsMs: [15 * minute, 60 * minute, 24 * 60 * minute],
      // Keep refreshing overdue reminders for one day instead of two weeks
      stopTimerRefreshBoundaryMs: 24 * 60 * minute,
    },
  },
});
```

`client.reminders.updateConfig({ scheduledOffsetsMs })` changes the same settings on the manager directly.

### The reminder label

Replace `ReminderNotification` through `WithComponents`. It receives the message's `Reminder`; read its state with `useStateStore`:

```tsx
import type { Channel as ChannelType, ReminderState } from "stream-chat";
import {
  Channel,
  WithComponents,
  useStateStore,
  type ReminderNotificationProps,
} from "stream-chat-react";

const reminderStateSelector = ({ remind_at, timeLeftMs }: ReminderState) => ({
  remindAt: remind_at,
  timeLeftMs,
});

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

  if (!reminder) return null;
  if (remindAt == null) return <div className="reminder">🔖 Saved</div>;

  const minutes = Math.round((timeLeftMs ?? 0) / 60000);
  return (
    <div className="reminder">
      ⏰ {minutes > 0 ? `In ${minutes} min` : "Due now"}
    </div>
  );
};

export const ChannelWithCustomReminders = ({
  channel,
}: {
  channel: ChannelType;
}) => (
  <WithComponents
    overrides={{ ReminderNotification: CustomReminderNotification }}
  >
    <Channel channel={channel}>{/* MessageList, MessageComposer */}</Channel>
  </WithComponents>
);
```

To read a message's reminder anywhere inside `Chat`, call `useMessageReminder(message.id)`. It returns the `Reminder`, or `undefined` when the message has none.

### Your own reminder list

A "Saved for later" list, following the steps in [The reminder list](#the-reminder-list):

```tsx
import { useEffect } from "react";
import type { PaginatorState, ReminderResponseData } from "stream-chat";
import { useChatContext, useStateStore } from "stream-chat-react";

const reminderListSelector = ({
  hasMoreTail,
  isLoading,
  items,
}: PaginatorState<ReminderResponseData>) => ({ hasMoreTail, isLoading, items });

export const SavedForLaterList = () => {
  const { client } = useChatContext();
  const {
    hasMoreTail,
    isLoading,
    items = [],
  } = useStateStore(client.reminders.paginator.state, reminderListSelector);

  useEffect(() => {
    // Setting the filter empties the list, so set it before the first query
    client.reminders.paginator.filters = { remind_at: { $exists: false } };
    void client.reminders.queryNextReminders();
  }, [client]);

  return (
    <div className="saved-for-later">
      <ul>
        {items.map((reminder) => (
          <li key={reminder.message_id}>
            {reminder.message?.user?.name}: {reminder.message?.text}
          </li>
        ))}
      </ul>
      {hasMoreTail && (
        <button
          disabled={isLoading}
          onClick={() => void client.reminders.queryNextReminders()}
        >
          Load more
        </button>
      )}
    </div>
  );
};
```

For a list of scheduled reminders, use `{ remind_at: { $exists: true } }` and sort by the deadline with `client.reminders.paginator.sort = [{ field: "remind_at", direction: 1 }]`.

### Alerting when a reminder is due

Listen for `notification.reminder_due` and publish a notification. Render this component once, inside `Chat`:

```tsx
import { useEffect } from "react";
import { useChatContext } from "stream-chat-react";

export const ReminderDueAlerts = () => {
  const { client } = useChatContext();

  useEffect(() => {
    const { unsubscribe } = client.on("notification.reminder_due", (event) => {
      client.notifications.addInfo({
        message: `Reminder: ${event.reminder?.message?.text ?? "a saved message"}`,
        origin: { emitter: "ReminderDueAlerts" },
      });
    });
    return unsubscribe;
  }, [client]);

  return null;
};
```

[Notifications UI](https://getstream.io/chat/docs/sdk/react/ui/components/notifications-ui/) explains where notifications show.

## Reference

- [Reminder manager](https://getstream.io/chat/docs/sdk/react/reference/state/reminder-manager/): `client.reminders`, its state, methods and timers.
- [Reminder hooks](https://getstream.io/chat/docs/sdk/react/reference/hooks/reminder-hooks/): `useMessageReminder`.
- [ComponentContext](https://getstream.io/chat/docs/sdk/react/component-reference/channel/componentcontext/): the `ReminderNotification` override key.
- [Message actions](https://getstream.io/chat/docs/sdk/react/features/messaging/message-actions/): the Remind me and Save for later actions.
- [Message reminders in the JavaScript client](https://getstream.io/chat/docs/javascript/message-reminders/): the reminders API and the server setting.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/features/messaging/reminders-and-save-for-later/](https://getstream.io/chat/docs/sdk/react/features/messaging/reminders-and-save-for-later/).