# Channel Read State

This guide provides an overview of how channel read state is handled by default and how to customize it.

## The model

The SDK maintains channel read state for UI components inside `Channel` as `channelUnreadUiState`. It powers `UnreadMessagesSeparator`, `UnreadMessagesNotification`, and any custom components that rely on unread counts. When a channel opens and is marked read, `channelUnreadUiState` intentionally does not update so users can still see how many unread messages were left from the previous session.

Channel read state reflecting the current back-end state can be accessed via `channel.state.read` mapping.

### Channel UI unread state

The state is maintained by `Channel` component and shared with its children via `ChannelStateContext` as `channelUnreadUiState`. The state format is as follows:

| Property                    | Type                    | Description                                                                                                                                                                                               |
| --------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **last_read**               | `Date`                  | Date when the channel was marked read the last time.                                                                                                                                                      |
| **unread_messages**         | `number`                | The count of unread messages in a given channel. Unread count refers only to foreign (not own) unread messages.                                                                                           |
| **first_unread_message_id** | `string` or `undefined` | The ID of the message that was marked unread (`notification.mark_unread` event). The value is available only when a message is marked unread. Therefore, cannot be relied on to place unread messages UI. |
| **last_read_message_id**    | `string` or `undefined` | The ID of the message preceding the first unread message.                                                                                                                                                 |

## Best Practices

- Use `markRead()` from `ChannelActionContext` to avoid rate-limit issues.
- Treat `channelUnreadUiState` as UI state, not authoritative backend state.
- Avoid marking read on mount if you need explicit user acknowledgement.
- Keep unread separators/notifications consistent across lists.
- Remember threads don’t affect channel read state.

### Channel read state for all users

Read state comes from the channel query response (`ChannelResponse.read`). It’s transformed from an array into an object keyed by user ID and updated on WS events like `message.read`, `notification.mark_unread`, and `message.new`. Each `read` entry has this shape:

| Property                    | Type                    | Description                                                                                                                                                                                                                                  |
| --------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **last_read**               | `Date`                  | Date when the channel was marked read the last time. The value is provided with `ChannelResponse` when querying channels or on `notification.mark_unread` event.                                                                             |
| **unread_messages**         | `number`                | The count of unread messages in a given channel for a given user. Unread count refers only to foreign (not own) unread messages. The value is provided with `ChannelResponse` when querying channels or on `notification.mark_unread` event. |
| **user**                    | `user`                  | Data of a user, whose read state is described in this object. The value is provided with `ChannelResponse` when querying channels or on `notification.mark_unread` event.                                                                    |
| **first_unread_message_id** | `string` or `undefined` | The ID of the message that was marked unread (`notification.mark_unread` event). The value is available only when a message is marked unread. Therefore, cannot be relied on to place unread messages UI.                                    |
| **last_read_message_id**    | `string` or `undefined` | The ID of the message preceding the first unread message. The value is provided with `ChannelResponse` when querying channels or on `notification.mark_unread` event.                                                                        |

<admonition type="note">

Be aware that only the last 100 newest messages can be marked unread. If older messages are marked unread, an error notification is shown informing about this limitation.

</admonition>

### Access the read state

In the SDK, the `read` and `channelUnreadUiState` can be accessed via the [`useChannelStateContext` hook](/chat/docs/sdk/react/components/contexts/channel_state_context#read/):

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

const Component = (props) => {
  const { client } = useChatContext();
  const { read, channel, channelUnreadUiState } = useChannelStateContext();

  // channel read state for some user
  const channelReadStateForAUser = read[props.user.id];

  // channel read state for own user
  const channelReadStateForMyUser = client.user && read[client.user.id];

  // easier way to access own user's unread count for a given channel
  const unreadCount = channel.unreadCount();

  //... code
};
```

### Mark a channel read

Channel can be marked read using the `markRead()` function provided by `ChannelActionContext` via the `useChannelActionContext` hook:

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

const MarkReadButton = (props) => {
  const { markRead } = useChannelActionContext();

  return (
    <button {...props} onClick={() => markRead()}>
      Mark read
    </button>
  );
};
```

The function accepts a single `options` parameter of the following format:

| Field                        | Type      | Optional | Description                                                                                                                                                                                                                                                                                                                        |
| ---------------------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `updateChannelUiUnreadState` | `boolean` | yes      | Signal, whether the `channelUnreadUiState` should be updated. The local state update is prevented when the Channel component is mounted. This is in order to keep the UI indicating the original unread state, when the user opens a channel. If the value for `updateChannelUiUnreadState` is not provided, the state is updated. |

<admonition type="tip">

Prefer `markRead()` within `Channel` because it throttles API calls and helps avoid mark-read rate limits.

</admonition>

## Default components involved in handling read state

The default components included in **marking a channel read**:

| Component                                                                                                                                                                     | Description                                                                                                                          |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| [`Channel`](/chat/docs/sdk/react/components/core-components/channel/)                                                                                                         | Can be configured to mark active channel read when mounted. This can be done through its prop `markReadOnMount`. By default enabled. |
| [`MessageList`](/chat/docs/sdk/react/components/core-components/message_list/), [`VirtualizedMessageList`](/chat/docs/sdk/react/components/core-components/virtualized_list/) | Marks channel read when message list is scrolled to the bottom.                                                                      |
| [`UnreadMessagesNotification`](/chat/docs/sdk/react/components/contexts/component_context#unreadmessagesnotification/)                                                        | Floating notification rendered in the message list. Contains a button, which when clicked, marks the channel read.                   |

The default components included in **marking a channel unread**:

| Component                                                                                                 | Description                                                                                                                                                                                                                                                                                 |
| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`MessageActionsBox`](/chat/docs/sdk/react/components/message-components/message_ui#message-actions-box/) | Action `Mark as Unread` is displayed if a user has `read-events` permission and the actions are displayed for a non-thread message posted by another user. Message which id matches that of the current user's read state's `first_unread_message_id` is not shown option `Mark as Unread`. |

The default components reflecting channel unread count:

| Component                                                                                                                                |
| ---------------------------------------------------------------------------------------------------------------------------------------- |
| [`ChannelPreviewMessenger`](/chat/docs/sdk/react/components/utility-components/channel_preview_ui/)                                      |
| [`UnreadMessagesSeparator`](/chat/docs/sdk/react/components/contexts/component_context#unreadmessagesseparator/)                         |
| [`UnreadMessagesNotification`](/chat/docs/sdk/react/components/contexts/component_context#unreadmessagesnotification/)                   |
| [`ScrollToBottomButton`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageList/ScrollToBottomButton.tsx) |

<admonition type="note">

Message threads do not participate in handling read state of a channel. Thread replies are not observed for unread count. Therefore, none of the UI components related to read state are rendered in threads.

</admonition>

## Default channel read state handling

The channel is marked read in the following scenarios:

1. User enters a channel with unread messages if `Channel` prop `markReadOnMount` is enabled (default behavior).
2. User scrolls up and back down to the latest message.
3. User clicks the button on the default `UnreadMessagesNotification` component to mark channel read.

The channel is marked unread in the following scenarios:

1. User with `read-events` permission selects `Mark as unread` option from the `MessageActionsBox`.

The component `UnreadMessagesSeparator` is shown immediately below the last read message. It can be followed by own message or a message posted by another user. It does not show unread count if:

- `showCount` prop is enabled and among the unread messages are only own messages (own message can be marked unread).
- `showCount` prop is disabled (default)

## Channel read state handling customization

### Customization through component props

There is a possibility to configure when a channel is marked read by tweaking these default components' props:

| Component                                                             | Prop                                   |
| --------------------------------------------------------------------- | -------------------------------------- |
| [`Channel`](/chat/docs/sdk/react/components/core-components/channel/) | `markReadOnMount` (by default enabled) |

### Customization through custom components

We can override the following components via `Channel`'s `ComponentContext`:

#### Custom `UnreadMessagesSeparator` component

Will be rendered before the first unread message.

```tsx
import { Channel, UnreadMessagesSeparatorProps } from "stream-chat-react";

const UnreadMessagesSeparator = (props: UnreadMessagesSeparatorProps) => {
  //... custom implementation
};
const Component = ({ children }) => (
  <Channel UnreadMessagesSeparator={UnreadMessagesSeparator}>
    {children}
  </Channel>
);
```

The component can be configured through the following props:

| Prop        | Description                                                                                     | Type      | Default |
| ----------- | ----------------------------------------------------------------------------------------------- | --------- | ------- |
| `showCount` | Configuration parameter to determine, whether the unread count is to be shown on the component. | `boolean` | `false` |

```tsx
import {
  UnreadMessagesSeparator as StreamUnreadMessagesSeparator,
  UnreadMessagesSeparatorProps,
} from "stream-chat-react";

const UnreadMessagesSeparator = (props: UnreadMessagesSeparatorProps) => {
  return <StreamUnreadMessagesSeparator {...props} showCount />;
};
```

#### Custom `UnreadMessagesNotification` component

Will be rendered only when `UnreadMessagesSeparator` is not visible in message list. The default implementation positions the notification as a floating element above the messages in a message list. It shows the number of unread messages since the user has scrolled away from the latest message (bottom of the message list).

```tsx
import { Channel, UnreadMessagesNotificationProps } from "stream-chat-react";

const UnreadMessagesNotification = (props: UnreadMessagesNotificationProps) => {
  //... custom implementation
};
const Component = ({ children }) => (
  <Channel UnreadMessagesNotification={UnreadMessagesNotification}>
    {children}
  </Channel>
);
```

The component can be configured through the following props:

| Prop                | Description                                                                                           | Type      | Default |
| ------------------- | ----------------------------------------------------------------------------------------------------- | --------- | ------- |
| `showCount`         | Configuration parameter to determine, whether the unread count is to be shown on the component.       | `boolean` | `false` |
| `queryMessageLimit` | Configuration parameter to determine the message page size, when jumping to the first unread message. | `number`  | `100`   |

```tsx
import {
  UnreadMessagesNotification as StreamUnreadMessagesNotification,
  UnreadMessagesNotificationProps,
} from "stream-chat-react";

const UnreadMessagesNotification = (props: UnreadMessagesNotificationProps) => {
  return (
    <StreamUnreadMessagesNotification
      {...props}
      queryMessageLimit={50}
      showCount
    />
  );
};
```

#### Custom `MessageNotification` component

The SDK exports [`ScrollToBottomButton`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageList/ScrollToBottomButton.tsx) that shows the unread count since the point the user has scrolled away from the newest messages in the list.

We can implement our own message notification component.

```tsx
import { MessageNotificationProps, Channel } from "stream-chat-react";

const MessageNotification = (props: MessageNotificationProps) => {
  //... custom implementation
};

const Component = ({ children }) => (
  <Channel MessageNotification={MessageNotification}>{children}</Channel>
);
```

## Jumping to the first unread message

### Default SDK component to jump to the first unread message

The SDK provides a component `UnreadMessagesNotification`, that when clicked on the part `Unread messages`, the message list scrolls to the first unread message. If the first unread message is not loaded in the local channel state, the message is retrieved from the API.

### API to jump to the first unread message

Use `jumpToFirstUnreadMessage()` function to implement custom UI to jump to the first unread message. The function takes one parameter `queryMessageLimit` that specifies the message page size if the message has to be loaded from the back-end API.


---

This page was last updated at 2026-03-06T17:06:46.896Z.

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