# Read state and unread counts

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

Every channel and every thread keeps a read state for each of its members: when they last read it, and how many messages they haven't read since.

>
> **Info:** **Example**
>
> The user last read "Design" on Monday at 9:00. Since then Bob and Cara posted 12 messages, so the user's read state says 12 unread. Bob's says 0, because posting a message counts as reading the channel.
>

The unread counts, unread markers and jumps to the first unread message that a UI shows all build on this state. Channels and threads keep it in different shapes, so this page covers channels first, then threads.

## Read state per user

A channel's read state is the `read` field of `channel.state`, one entry per user id:

| Field                                            | What it holds                                                                                                                                   |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `last_read`                                      | When the user last read the channel, in nanoseconds.                                                                                            |
| `unread_messages`                                | How many messages the user hasn't read.                                                                                                         |
| `last_read_message_id`                           | The last message the user read.                                                                                                                 |
| `first_unread_message_id`                        | The message the user marked the channel unread from.                                                                                            |
| `user`                                           | The user.                                                                                                                                       |
| `last_delivered_at`, `last_delivered_message_id` | Delivery receipts, explained in [Read state, unread and receipts](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/read-state-unread-and-receipts/). |

The current user's count is also `channel.state.unreadCount`, and `Channel.countUnread()` returns the same. Both read the current value without subscribing. To follow it, subscribe to `channel.state` and select the current user's entry:

```ts
import { nsToDate } from "stream-chat";

const ownUserId = client.userId;

const unsubscribe = channel.state.subscribeWithSelector(
  ({ read }) => ({ ownRead: ownUserId ? read[ownUserId] : undefined }),
  ({ ownRead }) => {
    if (!ownRead) return;
    const lastRead = nsToDate(ownRead.last_read).toLocaleString();
    console.log(`${ownRead.unread_messages} unread since ${lastRead}`);
  },
);
```

Timestamps are nanosecond numbers. `nsToDate` turns them into a `Date`, as [Data conventions](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/data-conventions/) explains.

A few more reads:

- `Channel.lastRead()` is the current user's `last_read`, or `null` if they never read the channel.
- `Channel.countUnread(lastRead)` counts the loaded newest messages after `lastRead` that count as unread.
- `Channel.countUnreadMentions()` counts the unread messages that mention the current user.

## What changes the read state

- **The state changes on its own, through events.** That includes changes the user makes on another device.
- **Other members' entries in `channel.state.read` update too.** When someone posts, their entry becomes read and everyone else's count goes up. Read receipts depend on this.
- **Channels the client doesn't watch still get the user's own read changes.** They don't get other members' read changes.
- **The server's count wins.** The client's count is an estimate, and each time the channel loads, the server's count replaces it.

## What counts as unread in a channel

When a new message arrives in a channel, it raises the user's count unless:

- the current user sent it;
- it's silent (`silent: true`) or shadowed by moderation;
- it's a thread reply that isn't shown in the channel;
- the current user muted its author;
- the current user muted the channel;
- the user is looking at the newest messages, as [Message paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/message-paginators/#live-view-and-unread) explains;
- the user doesn't have the `read-events` capability in the channel, unless the app counts unread messages locally (see [Turning read events off](#turning-read-events-off)).

`Channel.countUnread(lastRead)` and `Channel.countUnreadMentions()` follow the same rules, except the one about looking at the newest messages.

## Marking read and unread

Marking read or unread usually sends a request, and the read state changes when the server's event comes back, not when the call returns. Marking read locally changes it at once.

- **`Channel.markReadViaReporter()`** marks the channel read the way the client does on its own: through your `markReadRequest` handler if you registered one, or locally if the channel only counts unread messages locally. Use it when your UI marks a channel read.
- **`Channel.markRead()`** always sends the request, even when you registered a handler. With `{ message_id }`, it marks the channel read up to that message. Use it inside a `markReadRequest` handler. It throws when the channel isn't loaded or read events are off.
- **`Channel.markReadLocally()`** resets the current user's count without a request, for channels whose unread messages are only counted locally.
- **`Channel.markUnread({ message_id })`** marks the channel unread from that message. It takes `message_timestamp` instead of `message_id` too. Like `Channel.markRead()`, it throws when the channel isn't loaded or read events are off.

```ts
await channel.markUnread({ message_id: messageId });
// read[ownUserId].first_unread_message_id is set when notification.mark_unread arrives
```

To change how the client marks a channel read, register a `markReadRequest` handler. It receives the channel and the options, and returns the response, or `null` when it sent nothing:

```ts
client.config.set({
  channel: {
    requestHandlers: {
      markReadRequest: async ({ channel, options }) => {
        // Livestream channels are never marked read.
        if (channel.type === "livestream") return null;
        return await channel.markRead(options);
      },
    },
  },
});
```

[Message operations](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/message-operations/) explains request handlers. [What's optimistic and what isn't](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/whats-optimistic-and-what-isnt/) lists when each read action shows.

## Turning read events off

To turn read events off, turn off the channel type's `read_events` setting on the server. The user can then no longer mark the channel read, and nothing counts unread messages.

`readEvents.enabled: false` in `client.config` does less. It only stops this client from marking channels read: `Channel.markRead()` and `Channel.markUnread()` throw. The server and the client still count unread messages, so the user's count keeps going up. Use it for a client that shouldn't mark anything read, such as a moderation tool:

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

`channel.config.readEvents.enabled` is `false` when the channel type's `read_events` is off on the server, or `readEvents.enabled` is `false` in `client.config`.

A livestream channel type usually has `read_events` off on the server, so nothing counts unread messages. To count them anyway, turn on `localUnreadCountEnabled`. The client then counts new messages itself, and `Channel.markReadLocally()` resets the count. `Channel.markReadViaReporter()` calls it for you:

```ts
client.config.set({
  channel: { readEvents: { localUnreadCountEnabled: true } },
});

// The user has seen the newest messages.
channel.markReadLocally();
```

## Read state in threads

A thread keeps its own read state, in the `read` field of `thread.state`, one entry per participant. Its fields are named differently from a channel's:

| Field                  | What it holds                                       |
| ---------------------- | --------------------------------------------------- |
| `lastReadAt`           | When the user last read the thread, in nanoseconds. |
| `unreadMessageCount`   | How many replies the user hasn't read.              |
| `lastReadMessageId`    | The last reply the user read.                       |
| `firstUnreadMessageId` | The reply the user marked the thread unread from.   |
| `user`                 | The user.                                           |

`thread.ownUnreadCount` is the current user's count.

```ts
const ownUserId = client.userId;

const unsubscribe = thread.state.subscribeWithSelector(
  ({ read }) => ({
    unread: ownUserId ? (read[ownUserId]?.unreadMessageCount ?? 0) : 0,
  }),
  ({ unread }) => {
    console.log(`${unread} unread replies`);
  },
);
```

What changes it:

- a new reply: the author's entry goes to 0 unread, and every other entry gets 1 more, except the current user's while the thread is active;
- `message.read` for the thread: that user's entry goes to 0 unread;
- `notification.mark_unread` for the thread: the current user's entry takes the event's values.

A thread counts every new reply from someone else as unread, except while the thread is active. The channel's other rules (silent, shadowed, muted author or channel, read events off) don't apply to threads.

`Thread.markRead()` marks the thread read. It sends nothing when the current user has nothing unread, unless you pass `{ force: true }`. An active thread marks itself read when a reply arrives, at most once a second. To mark a thread unread, call `Channel.markUnread({ thread_id: thread.id })`.

Thread replies don't count toward the channel's unread count, and reading a thread doesn't change the channel's read state. The number of threads with unread replies is `unreadThreadCount` in `client.threads.state`, explained in [What channels and threads hold](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/#the-users-threads).

## Used in

- [Read state, unread and receipts](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/read-state-unread-and-receipts/)
- [Channel lists](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-lists/)
- [Replies and threads](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/replies-and-threads/)

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/read-state-and-unread-counts/](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/read-state-and-unread-counts/).