# What channels and threads hold

> **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 and a thread hold their conversations the same way. Each has its own state store, message paginator, composer, message operations and read state. A thread still belongs to a channel: it's identified by a message in that channel, and a few things, such as members, capabilities and settings, exist only on the channel.

>
> **Info:** **Example**
>
> Take the channel "Design", and the thread under the user's message "Release plan" in it. A reply the user types in the thread goes through the thread's composer into the thread's message paginator. The channel's message paginator and composer are untouched.
>

This page lists what they share, then what belongs only to a channel or only to a thread. Getting a channel or a thread, and loading it, is on [Shared entities](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/shared-entities/).

## What channels and threads share

|                    | Channel                     | Thread                                                                 |
| ------------------ | --------------------------- | ---------------------------------------------------------------------- |
| State              | `channel.state`             | `thread.state`                                                         |
| Message paginator  | `channel.messagePaginator`  | `thread.messagePaginator`                                              |
| Composer           | `channel.messageComposer`   | `thread.messageComposer`                                               |
| Message operations | `channel.messageOperations` | `thread.messageOperations`                                             |
| Read state         | `read` in `channel.state`   | `read` in `thread.state`                                               |
| Typing             | `typing` in `channel.state` | `typing` in `channel.state`, the entries with the thread's `parent_id` |

A thread belongs to its channel, `thread.channel`. `thread.state` has no `typing` field: who's typing in a thread is in the `typing` field of `channel.state`, which maps each typing user's id to their typing event. Keep the entries whose `parent_id` is the thread's id.

Each `state` is one store. Read a field once with `getLatestValue()`, or subscribe and select the fields you need:

```ts
const ownUserId = client.userId;

const unsubscribe = channel.state.subscribeWithSelector(
  ({ memberCount, read }) => ({
    memberCount,
    unread: ownUserId ? (read[ownUserId]?.unread_messages ?? 0) : 0,
  }),
  ({ memberCount, unread }) => {
    console.log(`${memberCount} members, ${unread} unread`);
  },
);
```

### Where each part is explained

- The message paginator: [Message paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/message-paginators/).
- The composer: [Message composer](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/message-composer/).
- Message operations: [Message operations](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/message-operations/).
- Read state: [Read state and unread counts](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/read-state-and-unread-counts/).
- Typing: [Typing indicators](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/typing-indicators/).
- What keeps a channel or a thread in memory: [Entity lifecycle](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/entity-lifecycle/).

## Channel

### Data and members

`channel.state` holds these fields:

| Field                        | What it holds                                                                                                                                                                                                         |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data`                       | The channel's server data: `frozen`, `hidden`, `cooldown`, `member_count`, `team`, `custom` and the other fields.                                                                                                     |
| `members`                    | The loaded members, by user id. A large channel loads only some of them: to search all of them, use a `ChannelMemberSearchSource`, see [Search sources](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/search-sources/). |
| `memberCount`                | The channel's total member count.                                                                                                                                                                                     |
| `membership`                 | The current user's own membership.                                                                                                                                                                                    |
| `watchers`, `watcherCount`   | Who is watching the channel right now.                                                                                                                                                                                |
| `watchStatus`                | Whether this client watches the channel: `watching`, `wasWatching` (a reconnect ended the watch, see [Connection](https://getstream.io/chat/docs/sdk/react/v15/state/connection/#channels-re-watched-on-demand)) or `notWatching`.        |
| `read`                       | Every member's read state.                                                                                                                                                                                            |
| `typing`                     | Who is typing.                                                                                                                                                                                                        |
| `ownCapabilities`            | What the current user may do in the channel.                                                                                                                                                                          |
| `muteStatus`                 | Whether the current user muted the channel.                                                                                                                                                                           |
| `initialized`, `offlineMode` | Whether the channel has been loaded from the server, or only from local data.                                                                                                                                         |
| `active`                     | Whether something has opened the channel, with `Channel.activate()`.                                                                                                                                                  |
| `aiState`                    | The state of an AI response in progress, see [SDK integration](https://getstream.io/chat/docs/sdk/react/v15/features/ai/sdk-integration/).                                                                                                |

`channel.data`, `channel.state.members` and the other getters on `channel` and `channel.state` return the value at the moment you read them. They don't tell you when it changes. To follow a change, subscribe to `channel.state` and select the field. [Reactive state](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/reactive-state/) explains why.

### Membership

`membership` is the current user's membership in the channel: their role, when they joined, and two dates the user sets for themselves:

- **`archived_at`** is set while the user has archived the channel;
- **`pinned_at`** is set while the user has pinned it.

`Channel.archive()`, `Channel.unarchive()`, `Channel.pin()` and `Channel.unpin()` change them. The change shows when the server's `member.updated` event arrives, not when the call returns. Channel paginators filtered on `archived` or `pinned` move the channel at the same moment.

```ts
const unsubscribe = channel.state.subscribeWithSelector(
  ({ membership }) => ({
    archived: !!membership.archived_at,
    pinned: !!membership.pinned_at,
  }),
  ({ archived, pinned }) => {
    console.log({ archived, pinned });
  },
);

await channel.archive(); // the handler runs again when member.updated arrives
```

### Capabilities

Capabilities say what the current user may do in this channel, such as `send-message`, `upload-file`, `send-reaction` or `skip-slow-mode`. The server derives them from the user's roles and the channel type's permissions.

They're in the `ownCapabilities` field of `channel.state`, and they update when the channel is loaded again or when `channel.updated` carries new ones. When a query or `Channel.updatePartial()` returns different capabilities, the client also dispatches `capabilities.changed`.

```ts
const canReact = channel.state
  .getLatestValue()
  .ownCapabilities.includes("send-reaction");
```

### Settings

Settings say which features the channel has, such as typing events, read events and replies. There are two views of them:

- **`channel.config`** is what's in effect: the channel type's settings combined with what the app registered in `client.config`. `channel.configState` is the same value as a store.
- **`channel.serverConfig`** is the channel type's settings as the server sent them: `typing_events`, `read_events`, `replies`, `uploads` and the others. It's `undefined` until the channel has been loaded.

Check `channel.config`, not `channel.serverConfig`. Either side can turn a feature off, and only `config` includes both:

```ts
const { readEvents, replies, typingEvents } = channel.config;

console.log({
  readEvents: readEvents.enabled,
  replies: replies.enabled,
  typingEvents: typingEvents.enabled,
});
```

[Configuration](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/configuration/) explains which settings the client decides, which the server decides, and which need both.

### Mute

The `muteStatus` field of `channel.state` says whether the current user muted the channel: `{ muted, createdAt, expiresAt }`, with the dates in nanoseconds or `null`.

`Channel.mute()` and `Channel.unmute()` change it. `Channel.mute({ expiration })` mutes for that many milliseconds. Like membership, the field changes when the server's event arrives, `notification.channel_mutes_updated`. A muted channel's new messages don't count as unread.

### Slow mode

In slow mode, a user waits a number of seconds between messages. The channel's `data.cooldown` holds the number of seconds, and `Channel.enableSlowMode(seconds)` and `Channel.disableSlowMode()` change it.

Each channel has a cooldown timer, `channel.cooldownTimer`. Its `state` counts down for the current user:

| Field                       | What it holds                                              |
| --------------------------- | ---------------------------------------------------------- |
| `cooldownConfigSeconds`     | The channel's cooldown, in seconds. `0` without slow mode. |
| `cooldownRemaining`         | Whole seconds until the user can send again.               |
| `canSkipCooldown`           | Whether the user has the `skip-slow-mode` capability.      |
| `ownLatestMessageTimestamp` | When the user last sent a message, in nanoseconds.         |

```ts
const unsubscribe = channel.cooldownTimer.state.subscribeWithSelector(
  ({ cooldownRemaining }) => ({ cooldownRemaining }),
  ({ cooldownRemaining }) => {
    console.log(
      cooldownRemaining > 0 ? `Wait ${cooldownRemaining}s` : "You can send",
    );
  },
);
```

[Slow mode](https://getstream.io/chat/docs/sdk/react/v15/features/channels/slow-mode/) shows how the composer uses it.

## Thread

### Parent message and replies

A thread is identified by its parent message: `thread.id` is the parent message's id. `thread.state` holds:

| Field                       | What it holds                                                      |
| --------------------------- | ------------------------------------------------------------------ |
| `parentMessage`             | The message the thread replies to.                                 |
| `replyCount`                | How many replies the thread has.                                   |
| `participants`              | The users taking part in the thread.                               |
| `read`                      | Every participant's read state in the thread.                      |
| `channel`                   | The channel the thread belongs to.                                 |
| `title`, `custom`           | The thread's own data.                                             |
| `active`                    | Whether something has opened the thread, with `Thread.activate()`. |
| `isLoading`, `isStateStale` | Whether the thread is loading, and whether it needs loading again. |

The parent message is the same message object the channel's message paginator holds. An edit or a reaction on it shows in both places, and `parentMessage` and `replyCount` follow it.

The replies are in `thread.messagePaginator`. A reply also sent to the channel, with `show_in_channel`, is in the channel's message paginator too.

`client.threads.ensure({ channel, parentMessage })` returns the thread for a message, creating it if the client doesn't have it yet, without a request. A thread created this way knows only its parent message. When it may have replies the client doesn't hold, it loads them the first time it's activated, which the UI does when it opens the thread:

```ts
const thread = client.threads.ensure({ channel, parentMessage: message });

// The user opened the thread. Call release() when they close it.
const release = thread.activate();
```

While a thread is active:

- a new reply doesn't count as unread for the current user, and the thread marks itself read;
- when its state is stale, it loads again.

These happen only while the thread manager, `client.threads`, listens to events: after `ThreadManager.registerSubscriptions()`. The React SDK calls it for you.

## The user's threads

The thread manager, `client.threads`, keeps the user's threads. Its paginator, `client.threads.paginator`, loads the threads, and `client.threads.state` holds what applies to all of them:

| Field                | What it holds                                                                                                                    |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `unreadThreadCount`  | How many of the user's threads have unread replies. The server sends this number, so it counts threads the client hasn't loaded. |
| `unseenThreadIds`    | Threads that got a new reply since the thread paginator last loaded, and aren't in it.                                           |
| `isThreadOrderStale` | Whether a thread in the paginator got a reply, so their order may be out of date.                                                |

`ThreadManager.reload()` loads the thread paginator again when it hasn't loaded yet, when there are unseen threads, or when the order is stale, and clears both once the load succeeds. `ThreadManager.reload({ force: true })` loads it in any case.

```ts
const unsubscribe = client.threads.state.subscribeWithSelector(
  ({ unreadThreadCount, unseenThreadIds }) => ({
    unreadThreadCount,
    unseenThreadIds,
  }),
  ({ unreadThreadCount, unseenThreadIds }) => {
    console.log(
      `${unreadThreadCount} unread threads, ${unseenThreadIds.length} new`,
    );
  },
);
```

The thread list itself is on [Replies and threads](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/replies-and-threads/), and its API on [Thread and thread manager](https://getstream.io/chat/docs/sdk/react/v15/reference/state/thread-and-thread-manager/).

## Used in

- [Channel details and members](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-details-and-members/)
- [Replies and threads](https://getstream.io/chat/docs/sdk/react/v15/features/messaging/replies-and-threads/)
- [Slow mode](https://getstream.io/chat/docs/sdk/react/v15/features/channels/slow-mode/)
- [Flagging, muting, blocking and bans](https://getstream.io/chat/docs/sdk/react/v15/features/moderation-and-safety/flagging-muting-blocking-and-bans/)

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/).