# Unread messages

Users can have unread messages in a channel because new messages arrived while they were away, or because they explictly marked a message as unread.

## Marking a message as unread

You can mark a message as unread from client-side:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val channelClient = chatClient.channel(cid = "<channel_cid>")
channelClient.markUnread(messageId = "<message_id>").enqueue { result ->
    if (result is Result.Success) {
        // Message marked as unread
    } else {
        // Handle Result.Failure
    }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
await channel.markUnread({ message_id: "<message_id>" });
```

</codetabs-item>

</codetabs>

Or from server-side by providing a user id:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await channel.markUnread({ message_id: "<message_id>", user_id: "<user_id>" });
```

</codetabs-item>

</codetabs>

For performance reasons it's only possible to mark any of the last 100 messages of the channel as unread.

## Marking a channel as read

You can mark all messages in a channel as read like this on the client-side:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val channelClient = chatClient.channel(cid = "<channel_cid>")
channelClient.markRead().enqueue { result ->
    if (result is Result.Success) {
        // Channel marked as read
    } else {
        // Handle Result.Failure
    }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
await channel.markRead();
```

</codetabs-item>

<codetabs-item value="dart" label="Dart">

```dart
await channel.markRead();
```

</codetabs-item>

<codetabs-item value="swift" label="Swift">

```swift
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))

channelController.markRead()
```

</codetabs-item>

<codetabs-item value="unreal" label="Unreal">

```cpp
Channel->MarkRead();
```

</codetabs-item>

<codetabs-item value="java" label="Java">

```java
channelClient.markRead().enqueue(result -> {
  if (result.isSuccess()) {
    // Messages in the channel marked as read
  } else {
    // Handle result.error()
  }
});
```

</codetabs-item>

<codetabs-item value="unity" label="Unity">

```csharp
await message.MarkMessageAsLastReadAsync();
```

</codetabs-item>

</codetabs>

Or from server-side by providing a user id:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
await channel.markRead({ user_id: "foo" });
```

</codetabs-item>

<codetabs-item value="php" label="PHP">

```php
$mark = $channel->markRead('user-id');
```

</codetabs-item>

</codetabs>

## Last read message

The `last_read_message_id` field tells which was the last read message that a given user read in the channel.

## Read state

When you retrieve a channel from the API (e.g. using query channels), the read state for members is included in the response (up to 100 members, ordered by the most recent added, the current user's read state is always included).

The read state includes the number of unread messages, the last read message id, and when the user read the channel. An example:

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val channelClient = chatClient.channel(cid = "<channel_cid>")
channelClient.get(
    messageLimit = 10,
    memberLimit = 10,
    state = true
).enqueue { result ->
    if (result is Result.Success) {
        val channel = result.value
        val channelRead = channel.read.find { it.user.id == "<user_id>" }
        if (channelRead != null) {
            // When the user read the channel
            println(channelRead.lastRead)
            // ID of the last read message
            println(channelRead.lastReadMessageId)
            // Number of unread messages for the user in the channel
            println(channelRead.unreadMessages)
        }
    } else {
        // Handle Result.Failure
    }
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
const channel = client.channel("messaging", "test");
await channel.watch();

console.log(channel.state.read["<user id>"]);

{
  last_read: "2024-02-09T12:23:46.361362996Z";
  last_read_message_id: "sara-e9c0d141-b109-4116-baec-e76966ae712e";
  unread_messages: 3;
}
```

</codetabs-item>

</codetabs>

The last read message id is updated in the following events:

- `message.read`

- `notification.mark_read`

- `notification.mark_unread`

<codetabs>

<codetabs-item value="kotlin" label="Kotlin">

```kotlin
val channelClient = chatClient.channel(cid = "<channel_cid>")
channelClient.watch().enqueue()

// Any member read the channel
channelClient.subscribeFor<MessageReadEvent> { event ->
    println(event.lastReadMessageId)
    // Unread messages: 0
}

// Connected user read the channel
channelClient.subscribeFor<NotificationMarkReadEvent> { event ->
    println(event.lastReadMessageId)
    // Unread messages: 0
}

// Connected user marked a message as unread
channelClient.subscribeFor<NotificationMarkUnreadEvent> { event ->
    println(event.lastReadMessageId)
    // Unread messages
    println(event.unreadMessages)
}
```

</codetabs-item>

<codetabs-item value="javascript" label="JavaScript">

```js
const channel = client.channel("messaging", "test");
await channel.watch();

// Any member read the channel
channel.on("message.read", (event) => {
  console.log(event.last_read_message_id);
  // Unread messages: 0
});

// Connected user read the channel
channel.on("notification.message_read", (event) => {
  console.log(event.last_read_message_id);
  // Unread messages: 0
});

// Connected user marked a message as unread
channel.on("notification.message_unread", (event) => {
  console.log(event.last_read_message_id);
  // Unread messages
  console.log(event.unread_messages);
});
```

</codetabs-item>

</codetabs>

## Jump to last read message

This is how you can jump to the last read message inside a given channel:

<codetabs>

<codetabs-item value="javascript" label="JavaScript">

```js
const channel = client.channel("messaging", "test");
await channel.watch();

const lastReadMessageId = channel.state.read["<user id>"];
await channel.state.loadMessageIntoState(lastReadMessageId);

console.log(channel.state.messages);
```

</codetabs-item>

</codetabs>

## Control read state exposure

Please take in account that user's read state exposure can be controlled by user privacy settings:

<codetabs>

<codetabs-item value="json" label="JSON">

```json
// user object with privacy settings where read receipts are disabled
{
  // other user fields
  "privacy_settings": {
    "read_receipts": {
      "enabled": false
    }
  }
}
```

</codetabs-item>

</codetabs>

If `privacy_settings.read_receipts.enabled` is set to `false` , then the read state of this user will not be exposed to others. Additionally, read state related events, such as **message.read** and **notification.mark_read** , will not be delivered to others when this user reads messages.

---

This page was last updated at 2025-08-15T14:14:15.508Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/android/unread_messages/](https://getstream.io/chat/docs/android/unread_messages/).