# Channel and thread hooks

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

These hooks read the state of one channel: its capabilities, members, name and image, slow mode, typing users, and read state. Threads have no state hooks: read `thread.state` with `useStateStore`, as [Where the other behavior lives](#where-the-other-behavior-lives) shows. The hooks that find a thread or the thread list are `useThreadContext` and `useThreadListItemContext` on [State and entity hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/state-and-entity-hooks/), and `useThreadsContext` on [Loading data hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/loading-data-hooks/#usethreadscontext).

What a channel and a thread hold is on [What channels and threads hold](https://getstream.io/chat/docs/sdk/react-native/v10/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-native/v10/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-native/v10/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.

Most hooks here take the channel as an argument instead of reading it from context. They work in a channel list row as well as inside a `Channel`. Inside one, pass `useChannel()`.

## useChannelOwnCapabilities

Returns what the current user may do in a channel.

```ts
useChannelOwnCapabilities(channel?: Channel): string[] | undefined
```

- **Returns:** the current user's capabilities in the channel, such as `send-message` or `upload-file`. `undefined` without a channel.
- **Reads:** `channel.state`, field `ownCapabilities`.
- **Re-renders:** when `ownCapabilities` changes.
- **Needs:** nothing.

Capabilities are explained on [What channels and threads hold](https://getstream.io/chat/docs/sdk/react-native/v10/state/channels-and-threads/what-channels-and-threads-hold/#capabilities). Inside a `Channel`, `useOwnCapabilitiesContext()` returns the same capabilities as booleans, with the overrides of the `overrideOwnCapabilities` prop applied: see [OwnCapabilitiesContext](https://getstream.io/chat/docs/sdk/react-native/v10/component-reference/channel/owncapabilitiescontext/).

```tsx
import { Text } from "react-native";
import {
  useChannel,
  useChannelOwnCapabilities,
} from "stream-chat-react-native";

export const UploadHint = () => {
  const channel = useChannel();
  const capabilities = useChannelOwnCapabilities(channel);

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

## useCanEditChannel

Says whether the current user may change the channel's name and image.

```ts
useCanEditChannel(channel?: Channel): boolean
```

- **Returns:** `true` when the user has the `update-channel` capability. `false` without a channel.
- **Reads:** `channel.state`, field `ownCapabilities`.
- **Re-renders:** when `ownCapabilities` changes.
- **Needs:** nothing.

## useCanAddMembersToChannel

Says whether the current user may add members to the channel.

```ts
useCanAddMembersToChannel(channel?: Channel): boolean
```

- **Returns:** `true` when the user has the `update-channel-members` capability. `false` without a channel.
- **Reads:** `channel.state`, field `ownCapabilities`.
- **Re-renders:** when `ownCapabilities` changes.
- **Needs:** nothing.

## useChannelMembersState

Returns the members of a channel.

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

`pick` is `(member) => Record<string, unknown>`. It names the fields of a member the component reads.

- **Returns:** the loaded members, keyed by user ID. `undefined` without a channel.
- **Reads:** `channel.state`, field `members`.
- **Re-renders:** without `pick`, whenever `members` changes. A member coming online or going offline counts as a change, because it replaces that member's object. With `pick`, only when a member joins or leaves, or a picked field changes for some member.
- **Needs:** nothing.

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

Define `pick` at module scope. A new function on each render builds a new selector, and the hook subscribes again:

```tsx
import { Text } from "react-native";
import type { Channel, ChannelMemberResponse } from "stream-chat";
import { useChannelMembersState } from "stream-chat-react-native";

// Ignores presence changes, which would re-render a list of names.
const pickName = (member: ChannelMemberResponse) => ({
  name: member.user?.name,
});

export const MemberNames = ({ channel }: { channel: Channel }) => {
  const members = useChannelMembersState(channel, pickName);

  return (
    <Text>
      {Object.values(members)
        .map((member) => member.user?.name ?? member.user_id)
        .join(", ")}
    </Text>
  );
};
```

## useIsChannelMember

Says whether a user is a member of a channel.

```ts
useIsChannelMember(channel: Channel, userId?: string): boolean
```

- **Returns:** `true` when one of the loaded members has that user ID. `false` without a user ID.
- **Reads:** `channel.state`, field `members`.
- **Re-renders:** whenever `members` changes, presence changes included.
- **Needs:** nothing.

It looks only at the loaded members, so it can return `false` for a member of a large channel whose member list is partly loaded.

## 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, which the client applies from query responses and from `member.added` and `member.updated`.
- **Needs:** nothing.

Membership is explained on [What channels and threads hold](https://getstream.io/chat/docs/sdk/react-native/v10/state/channels-and-threads/what-channels-and-threads-hold/#membership). `useIsChannelPinned(channel)`, on [Channel preview hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/channel-preview-hooks/#useischannelpinned), reads `pinned_at` from it.

## useChannelMemberCount

Returns the number of members of a channel.

```ts
useChannelMemberCount(channel: Channel): number
useChannelMemberCount(channel?: Channel): number | undefined
```

- **Returns:** the member count the server reports, including members not loaded. `undefined` without a channel.
- **Reads:** `channel.state`, field `memberCount`.
- **Re-renders:** when `memberCount` changes.
- **Needs:** nothing.

## useChannelOnlineMemberCount

Returns the number of users watching a channel.

```ts
useChannelOnlineMemberCount(channel: Channel): number
useChannelOnlineMemberCount(channel?: Channel): number | undefined
```

- **Returns:** the channel's watcher count: the users who have the channel open now. `undefined` without a channel.
- **Reads:** `channel.state`, field `watcherCount`.
- **Re-renders:** when `watcherCount` changes.
- **Needs:** nothing.

The channel details screen and `ChannelDetailsBottomSheet` show it as the "online" count of a group channel.

## useChannelName

Returns the name a channel was given.

```ts
useChannelName(channel?: Channel): string | undefined
```

- **Returns:** `channel.data.custom.name`, or `undefined` when the channel has no name.
- **Reads:** `channel.state`, field `data`.
- **Re-renders:** when the name changes.
- **Needs:** nothing.

It returns only the name set on the channel. For the name the SDK shows, which falls back to the members' names, use `useChannelPreviewDisplayName` on [Channel preview hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/channel-preview-hooks/#usechannelpreviewdisplayname).

## useChannelImage

Returns the image a channel was given.

```ts
useChannelImage(channel?: Channel): string | undefined
```

- **Returns:** `channel.data.custom.image`, or `undefined` when the channel has no image.
- **Reads:** `channel.state`, field `data`.
- **Re-renders:** when the image changes.
- **Needs:** nothing.

## useIsDirectChat

Says whether a channel is a conversation with one other user.

```ts
useIsDirectChat(channel?: Channel): boolean
```

- **Returns:** `true` when the loaded members include exactly one user other than the current user. `false` without a channel.
- **Reads:** `channel.state`, field `members`, with a `pick` of the user ID, and the current user's ID.
- **Re-renders:** when a member joins or leaves.
- **Needs:** `Chat`. Throws outside it.

The SDK uses it to pick between the labels and actions of a direct chat and those of a group, such as "Delete Chat" or "Delete Group".

## useChannelMuteActive

Says whether a channel shows as muted.

```ts
useChannelMuteActive(channel: Channel): boolean
```

- **Returns:** in a group channel, whether the current user muted the channel. In a direct chat, whether the user muted the channel or muted the other member.
- **Reads:** `channel.state`, fields `muteStatus` and `members`, and `client.mutedUsersStore` for the other member.
- **Re-renders:** when the channel's mute status changes, when the other member is muted or unmuted, and when a member joins or leaves.
- **Needs:** `Chat`. Throws outside it.

## 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 of the `Channel` or `Thread`. 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.

The default composer shows `CooldownTimer` in place of the send button while this is above `0`. Slow mode is explained on [What channels and threads hold](https://getstream.io/chat/docs/sdk/react-native/v10/state/channels-and-threads/what-channels-and-threads-hold/#slow-mode) and [Slow mode](https://getstream.io/chat/docs/sdk/react-native/v10/features/channels/slow-mode/). The [Composer hooks examples](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/composer-hooks/#examples) use it in a send button.

## useTypingString

Returns the text that says who is typing.

```ts
useTypingString(): string
```

- **Returns:**
  - `''` when nobody else is typing;
  - "Bob is typing" for one user, from the `channelPreview.typing.withUser.label` key;
  - "Bob and 2 more are typing" for more, from the `messageList.typing.withMoreUsers.label` key.

  It uses each user's `name`, or their ID. The current user is left out.

- **Reads:** `channel.state`, field `typing`. Inside a `Thread`, it counts only the users typing in that thread. Inside a `Channel`, only those typing in the channel's main list.
- **Re-renders:** when `typing` changes, and when the language changes.
- **Needs:** `Chat`, and a `Channel` or a `Thread`. Throws outside them.

The SDK has no channel header: the hook is for a header of your own, and the SDK's components don't call it. Typing is explained on [Typing indicators](https://getstream.io/chat/docs/sdk/react-native/v10/features/messaging/typing-indicators/). For a channel list row, which has no `Channel` around it, use `useChannelTypingState` on [Channel preview hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/channel-preview-hooks/#usechanneltypingstate).

## useChannelTypingUsers

Returns the users typing in a channel.

```ts
useChannelTypingUsers(channel: Channel, threadId?: string): UserResponse[]
```

- **Returns:** the users typing in the channel's main list, or, with `threadId`, in that thread. The current user is left out.
- **Reads:** `channel.state`, field `typing`.
- **Re-renders:** when `typing` changes.
- **Needs:** `Chat`. Throws outside it.

It takes the channel as an argument, so it works in a channel list row. `useChannelTypingState` on [Channel preview hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/channel-preview-hooks/#usechanneltypingstate) is built on it.

## useTypingUsers

Returns the users typing where the component sits.

```ts
useTypingUsers(): UserResponse[]
```

- **Returns:** `useChannelTypingUsers` for the channel of the `Channel` or `Thread`. Inside a `Thread`, the users typing in that thread.
- **Reads:** `channel.state`, field `typing`.
- **Re-renders:** when `typing` changes.
- **Needs:** `Chat`, and a `Channel` or a `Thread`. Throws outside them.

`TypingIndicator` shows these users. Use the hook for a typing indicator of your own, for example one with avatars. For the text that says who is typing, use `useTypingString`.

## useMarkRead

Returns the function that marks a channel read the way the SDK does.

```ts
useMarkRead(channel: Channel): (options?: { updateChannelUnreadState?: boolean }) => void
```

- **Returns:** a function with a stable identity. Each call:
  1. does nothing when the channel is pending disposal;
  2. when read events are off for the channel, calls `Channel.markReadLocally()` if local unread counts are on, and stops;
  3. otherwise reports the read through `client.messageDeliveryReporter.throttledMarkRead(channel)`, which throttles the requests and uses a `markReadRequest` handler when you registered one;
  4. unless `updateChannelUnreadState` is `false`, clears the unread boundary of `channel.messagePaginator`, so the unread separator and the unread notification go away.
- **Reads:** no store. It reads the channel's read settings when it's called.
- **Re-renders:** never by itself.
- **Needs:** `Chat`. Throws outside it.

`Channel`, `MessageList` and `MessageFlashList` mark the channel read with it. [Read state and unread counts](https://getstream.io/chat/docs/sdk/react-native/v10/state/channels-and-threads/read-state-and-unread-counts/#marking-read-and-unread) has a "Mark as read" button built on it.

## useOwnUnreadCount

Returns the current user's unread count in a channel.

```ts
useOwnUnreadCount(channel: Channel | undefined): number | undefined
```

- **Returns:** the `unread_messages` of the current user's read state in the channel, `0` when the user has none. `undefined` without a channel.
- **Reads:** `channel.state`, field `read`.
- **Re-renders:** when the current user's unread count changes. Reads of other members don't re-render.
- **Needs:** `Chat`. Throws outside it.

`ScrollToBottomButton` shows it in its badge in a channel. It takes the channel as an argument, so it works in a channel list row. Read state is explained on [Read state and unread counts](https://getstream.io/chat/docs/sdk/react-native/v10/state/channels-and-threads/read-state-and-unread-counts/).

## Where the other behavior 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`. Set its filters, sort and page size there. See [Channel list paginators](https://getstream.io/chat/docs/sdk/react-native/v10/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-native/v10/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 current user's count is `useOwnUnreadCount`, above.                                                                          |
| Watchers                                              | `channel.state`, `watchers` and `watcherCount`                                                                                                            |
| Other channel data                                    | `channel.state`, `data`                                                                                                                                   |
| Whether the channel has loaded and is watched         | `channel.state`, `initialized` and `watchStatus`                                                                                                          |
| The latest message                                    | `channel.messagePaginator.aggregateState`, `latestMessage`                                                                                                |
| Delivery and read receipts                            | `channel.messageReceiptsTracker.snapshotStore`. The receipt hooks are on [Message hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/message-hooks/). |
| A thread's parent message, reply count and read state | `thread.state`, `parentMessage`, `replyCount` and `read`                                                                                                  |
| A thread's replies                                    | `thread.messagePaginator.state`, `items`                                                                                                                  |
| 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-native/v10/reference/state/channel-and-channel-state/) and [Thread and thread manager](https://getstream.io/chat/docs/sdk/react-native/v10/reference/state/thread-and-thread-manager/). The AI state, `aiState`, has its own hook on [AI hooks](https://getstream.io/chat/docs/sdk/react-native/v10/reference/hooks/ai-hooks/).

A thread's unread count for the current user:

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

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 <Text>{unread}</Text>;
};
```

---

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