# Message Reminders

Message reminders let users revisit important messages later. With a timestamp, the user receives a notification at the scheduled time. Without one, the reminder acts like a bookmark.

## Best Practices

- Enable Push V3 and the reminder event before testing reminder delivery.
- Keep reminder offsets limited to reduce UI clutter.
- Use `useMessageReminder` for per-message updates instead of manual polling.
- Sort reminders by `remind_at` for predictable list ordering.
- Clear reminder indicators when a reminder is deleted or expired.

## Push Notifications Setup

Reminders require Push V3. In the Stream Dashboard, go to `Push Notifications` and click `Upgrade to V3`.

![Push V3](@chat-sdk/react-native/v8/_assets/guides/message-reminders/push-v3.png)

Then, in each configuration, enable `notification.reminder_due` in `Configure Push Notification Templates`.

## Message Reminder UI Interaction

Users create, update, or delete reminders from the message actions menu.

The reminder indicator appears in the message UI (or disappears when deleted).

## Get Message Reminder Data

The SDK uses `client.reminders` for CRUD. It stores a reactive map of message IDs to `Reminder` instances. Each `Reminder` extends `ReminderResponse` with `timeLeftMs`.

```ts
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;
};
```

1. Get a reminder for a specific message

Use `useMessageReminder` to get the `Reminder` instance for a message ID.

You can subscribe at two levels:

1. Subscribe to a specific reminder’s state

```tsx
import { useMessageReminder, useStateStore } from "stream-chat-react-native";
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 more frequently as the deadline approaches (daily → hourly → minutely) and less frequently afterward.

2. Subscribe to reminders pagination

Pagination is handled by `RemindersPaginator`. Use `useQueryReminders` to fetch pages and react to updates and deletions.

Pagination returns `ReminderResponse` objects, not `Reminder` instances.

```tsx
import { useCallback, useEffect } from "react";
import { FlatList, StyleSheet, Text, View } from "react-native";
import {
  useChatContext,
  useMessageReminder,
  useQueryReminders,
} from "stream-chat-react-native";
import { ReminderItem } from "./ReminderItem";

const renderItem = ({ item }: { item: ReminderResponse }) => (
  <ReminderItem {...item} />
);

const renderEmptyComponent = (
  <Text style={styles.emptyContainer}>No reminders available</Text>
);

const RemindersList = () => {
  const { client } = useChatContext();
  const { data, isLoading, loadNext } = useQueryReminders();

  useEffect(() => {
    client.reminders.paginator.filters = {};
    client.reminders.paginator.sort = { remind_at: 1 };
  }, [client.reminders]);

  const onRefresh = useCallback(async () => {
    await client.reminders.queryNextReminders();
  }, [client.reminders]);

  const renderFooter = useCallback(() => {
    if (isLoading) {
      return (
        <ActivityIndicator size={"small"} style={{ marginVertical: 16 }} />
      );
    }
  }, [isLoading]);

  return (
    <View style={{ flex: 1 }}>
      <FlatList
        contentContainerStyle={{ flexGrow: 1 }}
        data={data}
        keyExtractor={(item) => item.message.id}
        renderItem={renderItem}
        ListEmptyComponent={renderEmptyComponent}
        ListFooterComponent={renderFooter}
        onEndReached={loadNext}
      />
    </View>
  );
};
```

## Message Reminder Configuration

Configure which reminder offsets users can select:

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

You can also set when the reminder stops refreshing “time since due”:

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

## Reminder indicator on message

You can add a reminder indicator in the header by passing a custom `MessageHeader` to `Channel`:

![Message Reminder Header](@chat-sdk/react-native/v8/_assets/guides/message-reminders/reminder-header.png)

```tsx
import {
  MessageFooterProps,
  Time,
  useMessageReminder,
  useStateStore,
  useTranslationContext,
} from "stream-chat-react-native";
import { ReminderState } from "stream-chat";
import { StyleSheet, Text, View } from "react-native";

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

export const MessageReminderHeader = ({ message }: MessageFooterProps) => {
  const messageId = message?.id ?? "";
  const reminder = useMessageReminder(messageId);
  const { timeLeftMs } =
    useStateStore(reminder?.state, reminderStateSelector) ?? {};
  const { t } = useTranslationContext();

  const stopRefreshBoundaryMs = reminder?.timer.stopRefreshBoundaryMs;
  const stopRefreshTimeStamp =
    reminder?.remindAt && stopRefreshBoundaryMs
      ? reminder?.remindAt.getTime() + stopRefreshBoundaryMs
      : undefined;

  const isBehindRefreshBoundary =
    !!stopRefreshTimeStamp && new Date().getTime() > stopRefreshTimeStamp;

  if (!reminder) {
    return null;
  }

  // This is for "Saved for Later"
  if (!reminder.remindAt) {
    return (
      <View>
        <Text style={styles.headerTitle}>🔖 Saved for Later</Text>
      </View>
    );
  }

  if (reminder.remindAt && timeLeftMs !== null) {
    return (
      <View style={styles.headerContainer}>
        <Time height={16} width={16} />
        <Text style={styles.headerTitle}>
          {isBehindRefreshBoundary
            ? t("Due since {{ dueSince }}", {
                dueSince: t("timestamp/ReminderNotification", {
                  timestamp: reminder.remindAt,
                }),
              })
            : t("Due {{ timeLeft }}", {
                timeLeft: t("duration/Message reminder", {
                  milliseconds: timeLeftMs,
                }),
              })}
        </Text>
      </View>
    );
  }
};

const styles = StyleSheet.create({
  headerContainer: {
    flexDirection: "row",
    alignItems: "center",
  },
  headerTitle: {
    fontSize: 14,
    fontWeight: "500",
    marginLeft: 4,
  },
});
```


---

This page was last updated at 2026-04-17T17:33:45.361Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v8/guides/message-reminders/](https://getstream.io/chat/docs/sdk/react-native/v8/guides/message-reminders/).