# Pinned messages paginator

> **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/).

A channel's pinned messages are a list of their own: `channel.pinnedMessagesPaginator`, a `PinnedMessagePaginator`. Every channel creates one with itself, so apps never construct it. This page lists its API in `stream-chat`.

It's a list like the others, with the API listed in [The base paginator](https://getstream.io/chat/docs/sdk/react/v15/reference/state/channel-manager-and-channelpaginator/#the-base-paginator). The pinned messages as a feature, with their UI, are on [Pinned messages](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/pinned-messages/).

## State

`channel.pinnedMessagesPaginator.state` has the base paginator's fields:

| Field            | What it holds                                                                         |
| ---------------- | ------------------------------------------------------------------------------------- |
| `items`          | The loaded pinned messages, oldest pin first. `undefined` until the first page loads. |
| `isLoading`      | A page is loading.                                                                    |
| `hasMoreTail`    | There are earlier pins to load.                                                       |
| `hasMoreHead`    | There are later pins to load, after a jump.                                           |
| `lastQueryError` | The error of the last failed load. A successful load clears it.                       |

The list holds only messages with `pinned: true` that aren't shadowed, ordered by `pinned_at`. Its head is the most recent pin, at the end of `items`. To show the most recent pin first, reverse `items`.

Each pinned message is the same object the channel's message list holds. An edit, a reaction or a soft delete shows in both lists.

This handler logs the pins whenever they change, most recent first:

```ts
const { pinnedMessagesPaginator } = channel;

const unsubscribe = pinnedMessagesPaginator.state.subscribeWithSelector(
  ({ hasMoreTail, items }) => ({ hasMoreTail, items }),
  ({ hasMoreTail, items }) => {
    const latestFirst = [...(items ?? [])].reverse();
    console.log(
      latestFirst.map((message) => message.text),
      { hasMoreTail },
    );
  },
);
```

Unlike a channel's message list, the pinned list has no unread state, no live view and no `aggregateState`. It has no update throttle by default either: every change publishes at once.

## Loading more

The first page comes with the channel. When the channel is queried or watched, the response carries a page of its pinned messages. The first response's pins become the paginator's first page, so no separate request is needed. Pins from later responses are merged into the loaded pins without removing any, and don't move the paginator off a pin it jumped to.

That first page is measured against the paginator's page size. When the response carries fewer pins than a page, `hasMoreTail` is `false`, even if the channel has more pins than the response carried. Call `PinnedMessagePaginator.reload()` to load a full first page.

| Method                              | What it does                                                                          |
| ----------------------------------- | ------------------------------------------------------------------------------------- |
| `toTail(params)`                    | Loads earlier pins, through `Channel.getPinnedMessages()`.                            |
| `toHead(params)`                    | Loads later pins, after a jump.                                                       |
| `toTailDebounced(params)`           | `toTail()` once calls stop, for a scroll handler.                                     |
| `reload()`                          | Clears the list and loads its first page from `Channel.getPinnedMessages()`.          |
| `jumpToMessage(messageId, options)` | Shows the interval that holds the pinned message, loading a page around it if needed. |
| `jumpToTheLatestMessage(options)`   | Shows the most recent pins.                                                           |

The jumps take the options listed in [Jumping to a message](https://getstream.io/chat/docs/sdk/react/v15/reference/state/message-paginator/#jumping-to-a-message), and publish a focus signal in `pinnedMessagesPaginator.messageFocusSignal` the same way. A pinned message jumped to here is in the pinned paginator's interval. To show it in the conversation, jump the channel's message paginator too: `MessagePaginator.jumpToMessage(id)`.

A page holds 100 pins by default. Set the page size under `channel.pinnedMessagesPaginator` in `client.config`. The shared `messagePaginator` key doesn't apply to this list:

```ts
client.config.set({
  channel: { pinnedMessagesPaginator: { pageSize: 25 } },
});
```

The other [base options](https://getstream.io/chat/docs/sdk/react/v15/reference/state/channel-manager-and-channelpaginator/#configuration) go under the same key.

For a one-off look at the pinned messages, without a list that stays current, call `Channel.getPinnedMessages()`. It returns `{ messages }` and changes no state.

## What updates it

Pinning is a change to the message. `client.pinMessage(message)` and `client.unpinMessage(message)` change it on the server and update nothing locally. The list changes when the server's `message.updated` event arrives:

| Event                                                                           | What changes                                                                               |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `message.updated` with `pinned: true`                                           | The message joins the list at its `pinned_at` position.                                    |
| `message.updated` with `pinned: false`                                          | The message leaves the list.                                                               |
| `message.new` with `pinned: true`                                               | The message joins the list.                                                                |
| `message.updated`, `message.undeleted`, `message.deleted`                       | The message changes in place. A hard delete removes it.                                    |
| `user.messages.deleted`, and `user.deleted` when it deletes the user's messages | That user's pinned messages are marked deleted, or removed on a hard delete.               |
| `user.updated`                                                                  | That user's pinned messages get the new user object.                                       |
| `channel.truncated`                                                             | Pinned messages sent before the cutoff are removed. Without a cutoff, the list is emptied. |
| `channel.hidden` with `clear_history`                                           | The list is emptied.                                                                       |

A message deleted through `MessageOperations.delete()` changes at once, before the server answers. A hard delete removes it from the list.

`message.updated` skips replies. A reply that is pinned, with a `parent_id`, joins the list when the list loads it, not from the event.

This pins a message, and logs the pins once the event has added it:

```ts
const { pinnedMessagesPaginator } = channel;

const unsubscribe = pinnedMessagesPaginator.state.subscribeWithSelector(
  ({ items }) => ({ items }),
  ({ items }) => {
    console.log(`${items?.length ?? 0} pinned messages`);
  },
);

// Nothing changes locally until message.updated arrives.
await client.pinMessage(message);
```

## See also

- [Pinned messages](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/pinned-messages/) shows pinning in the UI.
- [How pagination works](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/how-pagination-works/) explains loading and staying live.
- [Message paginator](https://getstream.io/chat/docs/sdk/react/v15/reference/state/message-paginator/) lists the API of the channel's message list.
- [Channel and channel state](https://getstream.io/chat/docs/sdk/react/v15/reference/state/channel-and-channel-state/) lists the API of the channel.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/reference/state/pinned-messages-paginator/](https://getstream.io/chat/docs/sdk/react/v15/reference/state/pinned-messages-paginator/).