---
source_url: https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/unread
title: Unread messages
updated_at: 2026-06-04T11:09:04.570Z
---

> Documentation Index
> For a curated overview of PubNub documentation, see: https://www.pubnub.com/docs/llms.txt
> For the full list of all documentation pages, see: https://www.pubnub.com/docs/llms-full.txt


# Unread messages

Track unread message counts for users who reconnect after being offline.

`lastReadMessageTimetoken` on the [Membership](https://www.pubnub.com/docs/chat/chat-sdk/learn/chat-entities/membership) object stores when a user last read messages on a channel. This is set automatically on [join()](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/join) or [invite()](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/invite#invite-one-user). Update it based on user actions (scrolling, app focus, etc.) using [mark as read](#mark-messages-as-read-one-channel) methods.

:::note Requires App Context and Message Persistence
Enable [App Context](https://youtu.be/9UEoSlngpYI) and [Message Persistence](https://youtu.be/qLMtbINWGig) in the [Admin Portal](https://admin.pubnub.com/).
:::

## Get last read message

`lastReadMessageTimetoken` returns the timetoken marking the user's last read position on a channel. This timetoken doesn't always correspond to an actual message. Use it to display unread markers in your UI.

### Method signature

This method has the following signature:

```ts
membership.lastReadMessageTimetoken: string
```

#### Properties

| Property | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| lastReadMessageTimetoken | string | Optional |  | Value of the returned timetoken. |

### Sample code

Get the timetoken of the last message read by the `support_agent_15` user on the `support` channel.

```ts
// reference the "support_agent_15" user
const user = await chat.getUser("support_agent_15")

// get the list of all user memberships and filter out the right channel
const membership = await user.getMemberships({
    filter: "channel.id == 'support'"
})

// 
membership.lastReadMessageTimetoken
```

## Get unread messages count (one channel)

`getUnreadMessagesCount()` returns the number of unread messages on a channel. This counts all messages after the last read timetoken, including your own messages.

##### Under the hood

`getUnreadMessagesCount()` calls the Message Persistence API and the JavaScript SDK [messageCounts()](https://www.pubnub.com/docs/sdks/javascript/api-reference/storage-and-playback#message-counts) method.

### Method signature

This method has the following signature:

```ts
membership.getUnreadMessagesCount(): Promise<number | false>
```

#### Input

This method doesn't take any parameters.

#### Output

| Type | Description |
| --- | --- |
| `Promise<number>` or `Promise<false>` | Retrieves from Message Persistence the number of all messages unread by a given user on a given channel from the last read message. The method returns `false` when there is no [last read message timetoken](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/unread#mark-messages-as-read-one-channel) set on the [Message object](https://www.pubnub.com/docs/chat/chat-sdk/learn/chat-entities/message). |

### Sample code

Get the number of all messages unread by the `support_agent_15` user on the `support` channel.

```ts
// reference the "support_agent_15" user
const user = await chat.getUser("support_agent_15")

// get the list of all user memberships and filter out the right channel
const membership = await user.getMemberships({
    filter: "channel.id == 'support'"
})

// get the number of unread messages
await membership.getUnreadMessagesCount()
```

## Get unread messages count (all channels)

`fetchUnreadMessagesCounts()` returns unread counts for all joined channels with unread messages (channels with zero unread are excluded). Counts include all messages after the last read timetoken, including your own.

##### Under the hood

`fetchUnreadMessagesCounts()` calls the Message Persistence API and the JavaScript SDK [messageCounts()](https://www.pubnub.com/docs/sdks/javascript/api-reference/storage-and-playback#message-counts) method.

:::note Unread counts and filtering
Filters support `channel.*` fields plus `status`, `type`, `custom` (not `uuid.*`). Unread counts include your own messages. See [Memberships filters](https://www.pubnub.com/docs/general/metadata/filtering#memberships-filters).
:::

### Method signature

This method has the following signature:

```ts
chat.fetchUnreadMessagesCounts({
    filter?: string,
    sort?: object,
    limit?: number,
    page?: {
        next?: string,
        prev?: string
    }
}): Promise<{
        countsByChannel: Array<{
            channel: Channel;
            membership: Membership;
            count: number;
        }>;
        page: {
            next: string | undefined;
            prev: string | undefined;
        };
    }>
```

#### Input

| Parameter | Description |
| --- | --- |
| `filter`Type: `string` (Memberships filter)Default: n/a | Filter expression evaluated against [channel memberships](https://www.pubnub.com/docs/general/metadata/filtering#memberships-filters) only. Allowed targets: `channel.*` plus common fields (`status`, `type`, `custom`). `uuid.*` fields aren't supported. |
| `sort`Type: `object`Default: n/a | Key-value pair of a property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. Use `asc` or `desc` to specify the sorting direction, or specify `null` to take the default sorting direction (ascending). For example: `{name: "asc"}`. By default, the items are sorted by the last updated date. |
| `limit`Type: `number`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `object`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `> next`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `> prev`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |

#### Output

| Parameter | Description |
| --- | --- |
| `Promise<>`Type: `object` | Returned object containing these fields: `countsByChannel` and `page`. |
| `> countsByChannel`Type: `Array<object>` | Array of objects containing channel, membership, and count information. |
| `>> channel`Type: `Channel` | Channel with unread messages. |
| `>> membership`Type: `Membership` | Returned [Membership object](https://www.pubnub.com/docs/chat/chat-sdk/learn/chat-entities/membership) showing the user-channel data. |
| `>> count`Type: `number` | Total number of messages unread by the current user on a given channel. |
| `> page`Type: `object` | Object containing pagination information. |
| `>> next`Type: `string | undefined` | Token for fetching the next page of results. |
| `>> prev`Type: `string | undefined` | Token for fetching the previous page of results. |

### Sample code

Get the number of all messages unread by the `support_agent_15` user on all joined channels.

```ts
// reference the "support_agent_15" user
const user = await chat.getUser("support_agent_15")

// get all messages unread by that user
const result = await chat.fetchUnreadMessagesCounts()

// access the counts for each channel
const countsByChannel = result.countsByChannel

// access pagination tokens if needed
const { next, prev } = result.page
```

To avoid counting your own recently sent messages as unread, ensure your app updates the [last read timetoken](#mark-messages-as-read-one-channel).

## Mark messages as read (one channel)

`setLastReadMessage()` and `setLastReadMessageTimetoken()` set the last read timetoken for counting unread messages. Bind these to user actions in your app.

##### Under the hood

`setLastReadMessage()` and `setLastReadMessageTimetoken()` call App Context API and the JavaScript SDK [setMemberships()](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#set-channel-memberships) method.

Setting the last read message for users lets you implement the [Read Receipts](https://www.pubnub.com/docs/chat/chat-sdk/build/features/messages/read-receipts) feature and monitor which channel member read which message.

### Method signature

These methods take the following parameters:

* setLastReadMessage() 1membership.setLastReadMessage(2 message: Message3): Promise<Membership>
* setLastReadMessageTimetoken() 1membership.setLastReadMessageTimetoken(2 timetoken: string3): Promise<Membership>

#### Input

| Parameter | Required by `setLastReadMessage()` | Required by `setLastReadMessageTimetoken()` | Description |
| --- | --- | --- | --- |
| `message`Type: `Message`Default: n/a | Yes | No | Last read message on a given channel with the timestamp that gets added to the [user-channel membership](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/membership#update) as the `custom` parameter called `lastReadMessageTimetoken`. |
| `timetoken`Type: `string`Default: n/a | No | Yes | Timetoken of the last read message on a given channel that gets added to the [user-channel membership](https://www.pubnub.com/docs/chat/chat-sdk/build/features/channels/membership#update) as the `custom` parameter called `lastReadMessageTimetoken`. |

#### Output

| Type | Description |
| --- | --- |
| `Promise<Membership>` | Returned `Membership` object updated with the `lastReadMessageTimetoken` custom parameter. |

### Sample code

Set the message with the `16200000000000001` timetoken as the last read message for the `support_agent_15` user on the `support` channel.

* setLastReadMessage() 1// reference the "support_agent_15" user2const user = await chat.getUser("support_agent_15")3 4// get the list of all user memberships and filter out the right channel5const membership = await user.getMemberships({6 filter: "channel.id == 'support'"7})8 9// reference the "support" channel10const channel = await chat.getChannel("support")11 12// return the message object with the "16200000000000001" timetoken13const message = await channel.getMessage("16200000000000001")14 15// assuming a single membership which matches the filter16await membership.memberships[0].setLastReadMessage(message)
* setLastReadMessageTimetoken() 1// reference the "support_agent_15" user2const user = await chat.getUser("support_agent_15")3 4// get the list of all user memberships and filter out the right channel5const membership = await user.getMemberships({6 filter: "channel.id == 'support'"7})8 9// reference the "support" channel10const channel = await chat.getChannel("support")11 12// return the message object with the "16200000000000001" timetoken13const message = await channel.getMessage("16200000000000001")14 15// assuming a single membership which matches the filter16await membership.memberships[0].setLastReadMessageTimetoken(message.timetoken)

## Mark messages as read (all channels)

`markAllMessagesAsRead()` marks all unread messages as read on all joined channels.

##### Under the hood

`markAllMessagesAsRead()` calls Message Persistence API and the JavaScript SDK [getMemberships()](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#channel-memberships), [setMemberships()](https://www.pubnub.com/docs/sdks/javascript/api-reference/objects#set-channel-memberships), and [fetchMessages()](https://www.pubnub.com/docs/sdks/javascript/api-reference/storage-and-playback#fetch-history) methods.

### Method signature

This method has the following signature:

```ts
chat.markAllMessagesAsRead({
    filter?: string,
    sort?: object,
    limit?: number,
    page?: {
        next?: string,
        prev?: string 
    }
}): Promise<{
        page: {
          next: string,
          prev: string
        },
        total: number,
        status: number,
        memberships: Membership[]
}>
```

#### Input

| Parameter | Description |
| --- | --- |
| `filter`Type: `string` (Memberships filter)Default: n/a | Expression used to filter the results. Returns only these messages whose properties satisfy the given expression. The filtering language is [defined here](https://www.pubnub.com/docs/general/metadata/filtering). |
| `sort`Type: `object`Default: n/a | Key-value pair of a property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. Use `asc` or `desc` to specify the sorting direction, or specify `null` to take the default sorting direction (ascending). For example: `{name: "asc"}`. By default, the items are sorted by the last updated date. |
| `limit`Type: `number`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `object`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `> next`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `> prev`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |

#### Output

| Parameter | Description |
| --- | --- |
| `Promise<>`Type: `object` | Returned object containing these fields: `page`, `total`, `status`, and `memberships`. |
| `> page`Type: `object` | Object used for pagination to define which previous or next result page you want to fetch. |
| `>> next`Type: `string` | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `>> prev`Type: `string` | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |
| `> total`Type: `number` | Total number of messages marked as read. |
| `> status`Type: `number` | Status code of a server response, like `200`. |
| `> memberships`Type: `Membership[]` | List of all related memberships. |

### Sample code

Mark the total number of 50 messages as read and specify you want to fetch the results from the next page using a string that was previously returned from the PubNub server.

```ts
await chat.markAllMessagesAsRead(
    {
        limit: 50,
        page: {
            next: "<returned_random_string>"
        }
    }
)
```

## Get unread messages count (all channels) (deprecated)

:::warning Deprecated
Use [fetchUnreadMessagesCounts()](#get-unread-messages-count-all-channels) instead.
:::

`getUnreadMessagesCounts()` returns unread counts for all joined channels.

##### Under the hood

`getUnreadMessagesCounts()` calls the Message Persistence API and the JavaScript SDK [messageCounts()](https://www.pubnub.com/docs/sdks/javascript/api-reference/storage-and-playback#message-counts) method.

### Method signature

This method has the following signature:

```ts
chat.getUnreadMessagesCounts({
    filter?: string,
    sort?: object,
    limit?: number,
    page?: {
        next?: string,
        prev?: string
    }
}): Promise<{
        channel: Channel,
        membership: Membership,
        count: number
    }[]>
```

#### Input

| Parameter | Description |
| --- | --- |
| `filter`Type: `string` (Memberships filter)Default: n/a | Filter expression evaluated against [channel memberships](https://www.pubnub.com/docs/general/metadata/filtering#memberships-filters) only. Allowed targets: `channel.*` plus common fields (`status`, `type`, `custom`). `uuid.*` fields aren't supported. |
| `sort`Type: `object`Default: n/a | Key-value pair of a property to sort by, and a sort direction. Available options are `id`, `name`, and `updated`. Use `asc` or `desc` to specify the sorting direction, or specify `null` to take the default sorting direction (ascending). For example: `{name: "asc"}`. By default, the items are sorted by the last updated date. |
| `limit`Type: `number`Default: `100` | Number of objects to return in response. The default (and maximum) value is `100`. |
| `page`Type: `object`Default: n/a | Object used for pagination to define which previous or next result page you want to fetch. |
| `> next`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for forward pagination, it fetches the next page, allowing you to continue from where you left off. |
| `> prev`Type: `string`Default: n/a | Random string returned from the server, indicating a specific position in a data set. Used for backward pagination, it fetches the previous page, enabling access to earlier data. Ignored if the `next` parameter is supplied. |

#### Output

| Parameter | Description |
| --- | --- |
| `Promise<>`Type: `object` | Returned object containing these fields: `channel`, `membership`, and `count`. The method returns an array of such objects corresponding to all channel memberships. |
| `> channel`Type: `Channel` | Channel with unread messages. |
| `> membership`Type: `Membership` | Returned [Membership object](https://www.pubnub.com/docs/chat/chat-sdk/learn/chat-entities/membership) showing the user-channel data. |
| `> count`Type: `number` | Total number of messages unread by the current user on a given channel. |

### Sample code

Get the number of all messages unread by the `support_agent_15` user on all joined channels.

```ts
// reference the "support_agent_15" user
const user = await chat.getUser("support_agent_15")

// get all messages unread by that user
await chat.getUnreadMessagesCounts()
```