# Channel and thread hooks

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

These hooks read the state of one channel: its capabilities, members, display name, slow mode and read receipts. Threads have no hooks of their own: read `thread.state` with `useStateStore`, as [Where the other behaviour lives](#where-the-other-behaviour-lives) shows.

What a channel and a thread hold is on [What channels and threads hold](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/), and read state on [Read state and unread counts](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/read-state-and-unread-counts/). How to read state in components is on [Reading state in components](https://getstream.io/chat/docs/sdk/react/v15/ui/reading-state-in-components/).

Each entry gives the signature, then:

- **Returns:** what the hook gives back.
- **Reads:** the store or the context it reads.
- **Re-renders:** what makes the component render again.
- **Needs:** where the component must sit, and what the hook does outside that place.

## useChannelCapabilities

Returns what the current user may do in the channel the component renders in.

```ts
useChannelCapabilities(params: { cid: string | undefined }): Set<string>
```

- **Returns:** the current user's capabilities in the channel from `useChannel()`, such as `send-message` or `upload-file`. An empty set when `cid` is `undefined` or isn't that channel's `cid`.
- **Reads:** `channel.state`, field `ownCapabilities`.
- **Re-renders:** when `ownCapabilities` changes.
- **Needs:** a `Channel` or a `Thread`. Throws outside both.

It only answers for the channel the component renders in. For another channel, such as a row in a channel list, select `ownCapabilities` from that channel's `state` with `useStateStore`. Capabilities are explained on [What channels and threads hold](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/#capabilities).

```tsx
import { useChannel, useChannelCapabilities } from "stream-chat-react";

export const UploadHint = () => {
  const channel = useChannel();
  const capabilities = useChannelCapabilities({ cid: channel.cid });

  if (capabilities.has("upload-file")) return null;
  return <span>You can't upload files in this channel.</span>;
};
```

## useChannelMembersState

Returns the members of a channel.

```ts
useChannelMembersState(channel: Channel): Record<string, ChannelMemberResponse>
useChannelMembersState(channel?: Channel): Record<string, ChannelMemberResponse> | undefined
```

- **Returns:** the loaded members, keyed by user ID. `undefined` without a channel.
- **Reads:** `channel.state`, field `members`.
- **Re-renders:** when `members` changes.
- **Needs:** nothing. It doesn't read the channel from context, so pass one.

`members` holds the members the channel has loaded, which can be fewer than all of them. The total is `memberCount` in `channel.state`.

## useChannelMembershipState

Returns the current user's membership in a channel.

```ts
useChannelMembershipState(channel: Channel): ChannelMemberResponse
useChannelMembershipState(channel?: Channel): ChannelMemberResponse | undefined
```

- **Returns:** the current user's member record: its `channel_role`, and `pinned_at` and `archived_at` when the user pinned or archived the channel. `undefined` without a channel.
- **Reads:** `channel.state`, field `membership`.
- **Re-renders:** when `membership` changes. Pinning and archiving change it when the `member.updated` event arrives, not when the call is made.
- **Needs:** nothing. It doesn't read the channel from context, so pass one.

A badge for a pinned channel in a channel list row:

```tsx
import type { Channel } from "stream-chat";
import { useChannelMembershipState } from "stream-chat-react";

export const PinnedBadge = ({ channel }: { channel: Channel }) => {
  const membership = useChannelMembershipState(channel);
  return membership.pinned_at ? <span>Pinned</span> : null;
};
```

Membership is explained on [What channels and threads hold](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/#membership).

## useChannelDisplayName

Returns the name to show for a channel.

```ts
useChannelDisplayName(channel: Channel | undefined): string | undefined
```

- **Returns**, the first that applies:
  1. the channel's `name`, from `channel.data.custom.name`;
  2. in a channel with two members, the other member's name, or the translated "Direct message" when that user has no name;
  3. in a channel with more members, the names of two other members, joined with a comma;
  4. `undefined`.
- **Reads:** `channel.state`, fields `data` and `members`, and the current user's ID.
- **Re-renders:** when the channel's `data` or `members` change.
- **Needs:** `Chat`. Throws outside it.

## useChannelPreviewInfo

Returns the title, image and group members a channel preview shows.

```ts
useChannelPreviewInfo(params: { channel?: Channel; overrideImage?: string; overrideTitle?: string }): { displayImage: string | undefined; displayTitle: string | undefined; groupChannelDisplayInfo: GroupChannelDisplayInfo }
```

- **Returns:**
  - `displayTitle`: `overrideTitle`, or else the name from `useChannelDisplayName`.
  - `displayImage`: `overrideImage`, or else `channel.data.custom.image`, or else in a channel with two members the other member's image.
  - `groupChannelDisplayInfo`: in a channel with more than two members, `{ members }` with the name and image of each member that has one. `{ members: [] }` otherwise.
- **Reads:** `channel.state`, fields `data` and `members`, and the current user's ID.
- **Re-renders:** when the channel's `data` or `members` change.
- **Needs:** `Chat`. Throws outside it. Without a channel, the title and image are the overrides you pass, or `undefined`.

```tsx
import type { Channel } from "stream-chat";
import { useChannelPreviewInfo } from "stream-chat-react";

export const ChannelTitle = ({ channel }: { channel: Channel }) => {
  const { displayImage, displayTitle } = useChannelPreviewInfo({ channel });

  return (
    <div className="channel-title">
      {displayImage && <img alt="" src={displayImage} />}
      <span>{displayTitle ?? "Unnamed channel"}</span>
    </div>
  );
};
```

## useCooldownRemaining

Returns the seconds left before the current user can send again in slow mode.

```ts
useCooldownRemaining(): number
```

- **Returns:** the seconds left, rounded. `0` when slow mode is off, when the user can skip it, or when the wait is over.
- **Reads:** `channel.cooldownTimer.state`, field `cooldownRemaining`, of the channel from `useChannel()`. Inside a `Thread`, that's the thread's channel: slow mode applies to the whole channel.
- **Re-renders:** every second while the user waits, and when slow mode changes.
- **Needs:** a `Channel` or a `Thread`. Throws outside both.

Slow mode is explained on [What channels and threads hold](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/#slow-mode) and [Slow mode](https://getstream.io/chat/docs/sdk/react/v15/features/channels/slow-mode/). The [Composer hooks examples](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/composer-hooks/#examples) use it in a send button.

## useIsCooldownActive

Returns whether the current user is waiting to send again in slow mode.

```ts
useIsCooldownActive(): boolean
```

- **Returns:** `true` while the seconds left are above `0`, `false` otherwise.
- **Reads:** `channel.cooldownTimer.state`, field `cooldownRemaining`, of the channel from `useChannel()`. Inside a `Thread`, that's the thread's channel.
- **Re-renders:** when the wait starts and when it ends. Unlike `useCooldownRemaining`, not every second.
- **Needs:** a `Channel` or a `Thread`. Throws outside both.

The composer uses it while the user waits: `MessageComposerActions` shows `CooldownTimer` in place of the send button, the emoji picker isn't rendered, `AttachmentSelector` is disabled, and the drop zone of `WithDragAndDropUpload` is turned off.

## useLastReadData

Returns who has read each message of a rendered list.

```ts
useLastReadData(params: { channel: Channel; messages: LocalMessage[]; returnAllReadData: boolean; lastOwnMessage?: LocalMessage }): Record<string, UserResponse[]>
```

- **Returns:** a map from message ID to the users whose read state is at or past that message.
  - `messages` are the messages you render, oldest first.
  - With `returnAllReadData: true`, the map has an entry for every message in `messages`.
  - With `returnAllReadData: false`, it has one entry, for `lastOwnMessage`, and is empty without it.
  - The lists can include the current user. Leave them out for a "seen by" label.
- **Reads:** `channel.messageReceiptsTracker.snapshotStore`.
- **Re-renders:** when the tracker publishes new read or delivery state.
- **Needs:** nothing. Pass the channel.

`MessageList` and `VirtualizedMessageList` call it with their `returnAllReadData` prop. The tracker is on [Message receipts tracker](https://getstream.io/chat/docs/sdk/react/v15/reference/state/message-receipts-tracker/), and the feature on [Read state, unread and receipts](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/read-state-unread-and-receipts/).

## Where the other behaviour lives

Querying, paging and reconnecting have no hooks. They belong to the state layer, so they work whether or not a component is mounted:

- **Querying and pagination** belong to the `ChannelPaginator` you pass to `ChannelList`. Configure its filters, sort and page size there rather than wrapping the list. See [Channel list paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/channel-list-paginators/).
- **Reconnect recovery** belongs to the client. After a reconnect it refreshes the loaded channel lists and reloads the open channels and threads, then dispatches `connection.recovered`. You don't need to listen for that event to refresh a list. See [Connection recovery](https://getstream.io/chat/docs/sdk/react/v15/state/connection/#connection-recovery).
- **Which lists exist** belongs to `client.channelManager`, which holds the paginators and decides which list owns a channel that matches several.

For the rest of a channel's or a thread's state, use `useStateStore` with your own selector:

| To read                                           | Store and field                                                                                                                    |
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Read state and the unread count                   | `channel.state`, `read`. The [`UnreadBadge` example](https://getstream.io/chat/docs/sdk/react/v15/ui/reading-state-in-components/) computes the count. |
| Typing users                                      | `channel.state`, `typing`                                                                                                          |
| Watchers                                          | `channel.state`, `watchers` and `watcherCount`                                                                                     |
| Name, image and other channel data                | `channel.state`, `data`                                                                                                            |
| The member count                                  | `channel.state`, `memberCount`                                                                                                     |
| Whether the channel is muted                      | `channel.state`, `muteStatus`                                                                                                      |
| Whether the channel has loaded and is watched     | `channel.state`, `initialized` and `watchStatus`                                                                                   |
| The latest message                                | `channel.messagePaginator.aggregateState`, `latestMessage`                                                                         |
| A thread's parent message, replies and read state | `thread.state`, `parentMessage`, `replyCount` and `read`                                                                           |
| The unread thread count                           | `client.threads.state`, `unreadThreadCount`                                                                                        |

The fields of these stores are on [Channel and channel state](https://getstream.io/chat/docs/sdk/react/v15/reference/state/channel-and-channel-state/) and [Thread and thread manager](https://getstream.io/chat/docs/sdk/react/v15/reference/state/thread-and-thread-manager/). The AI indicator state, `aiState`, has its own hook on [AI hooks](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/ai-hooks/).

A thread's unread count for the current user:

```tsx
import type { ThreadState } from "stream-chat";
import {
  useChatContext,
  useStateStore,
  useThreadContext,
} from "stream-chat-react";

const readSelector = (state: ThreadState) => ({ read: state.read });

export const ThreadUnreadCount = () => {
  const { client } = useChatContext();
  const thread = useThreadContext();
  const { read } = useStateStore(thread?.state, readSelector) ?? {};

  const unread = client.userID
    ? (read?.[client.userID]?.unreadMessageCount ?? 0)
    : 0;
  if (!unread) return null;

  return <span className="unread-badge">{unread}</span>;
};
```

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/channel-and-thread-hooks/](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/channel-and-thread-hooks/).