# Channel

The `Channel` component wraps the logic, state, and UI for a single chat channel. It provides four contexts to its children:

- [`ChannelStateContext`](/chat/docs/sdk/react/components/contexts/channel-state-context/) - stateful data (ex: `messages` or `members`)
- [`ChannelActionContext`](/chat/docs/sdk/react/components/contexts/channel-action-context/) - action handlers (ex: `sendMessage` or `openThread`)
- [`ComponentContext`](/chat/docs/sdk/react/components/contexts/component-context/) - SDK UI overrides registered with `WithComponents`
- [`TypingContext`](/chat/docs/sdk/react/components/message-composer/typing-context/) - object of currently typing users (i.e., `typing`)

## Best Practices

- Use `ChannelList` to manage active channels unless you need custom switching logic.
- Pass the `channel` prop only when you control selection; avoid it when using `ChannelList`.
- Register SDK UI overrides with `WithComponents`, and keep `Channel` focused on channel behavior and data loading.
- Set `channelQueryOptions` intentionally if your app depends on specific initial limits.
- Access context via hooks inside `Channel` children to avoid stale or missing data.

<admonition type="note">

`Channel` renders a single `channel` object. For details about channel objects, see the [JavaScript docs](/chat/docs/javascript/creating-channels/).

</admonition>

## Basic Usage

`Channel` doesn’t render UI on its own. You can use it with or without `ChannelList`:

- If you use `ChannelList`, don’t pass a `channel` prop. `ChannelList` sets the active channel.
- If you don’t use `ChannelList`, you must pass the `channel` prop.

**Example 1** - without `ChannelList`

```tsx
import { Channel, Chat, MessageComposer, MessageList } from "stream-chat-react";

const App = () => (
  <Chat client={client}>
    <Channel channel={channel}>
      <MessageList />
      <MessageComposer />
    </Channel>
  </Chat>
);
```

**Example 2** - with `ChannelList`

```tsx
import {
  Channel,
  ChannelList,
  Chat,
  MessageComposer,
  MessageList,
} from "stream-chat-react";

const App = () => (
  <Chat client={client}>
    <ChannelList />
    <Channel>
      <MessageList />
      <MessageComposer />
    </Channel>
  </Chat>
);
```

Any child of `Channel` can access these contexts via hooks:

```tsx
import {
  useChannelActionContext,
  useChannelStateContext,
  useComponentContext,
  useTypingContext,
} from "stream-chat-react";

const CustomChannelChild = () => {
  const { messages } = useChannelStateContext();
  const { sendMessage } = useChannelActionContext();
  const { Avatar } = useComponentContext();
  const { typing } = useTypingContext();

  return (
    <div>
      <span>{messages.length} messages loaded</span>
      <span>{Avatar ? "avatar override available" : "default avatar"}</span>
      <span>{Object.keys(typing).length} people typing</span>
    </div>
  );
};
```

## Registering Custom Components

Use `WithComponents` to replace SDK UI within a `Channel` subtree. `Channel` itself owns channel behavior, data loading, and action hooks. The SDK UI override surface is exposed through [`ComponentContext`](/chat/docs/sdk/react/components/contexts/component-context/).

```tsx
import {
  Channel,
  ChannelHeader,
  ChannelList,
  Chat,
  MessageComposer,
  MessageList,
  Thread,
  Window,
  WithComponents,
} from "stream-chat-react";
import { CustomTooltip } from "../Tooltip/CustomTooltip";

const CustomAvatar = ({ imageUrl, userName }) => (
  <>
    <CustomTooltip>{userName}</CustomTooltip>
    <div className="avatar-image">
      <img alt={userName} src={imageUrl} />
    </div>
  </>
);

const App = () => (
  <Chat client={client}>
    <ChannelList />
    <WithComponents overrides={{ Avatar: CustomAvatar }}>
      <Channel>
        <Window>
          <ChannelHeader />
          <MessageList />
          <MessageComposer />
        </Window>
        <Thread />
      </Channel>
    </WithComponents>
  </Chat>
);
```

You can still scope overrides narrowly when only one composer subtree should differ. For example, wrap a single `MessageComposer` in `WithComponents`:

```tsx
import { MessageComposer, WithComponents } from "stream-chat-react";

<Channel>
  <MessageList />
  <WithComponents overrides={{ MessageComposerUI: CustomMessageComposer }}>
    <MessageComposer />
  </WithComponents>
</Channel>;
```

## Props

| Prop                           | Description                                                                                                                                                                                                     | Type                                                                                                                                  |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `activeUnreadHandler`          | Custom handler that runs when the active channel has unread messages and the app is running in another browser tab.                                                                                             | `(unread: number, documentTitle: string) => void`                                                                                     |
| `allowConcurrentAudioPlayback` | Allows multiple audio players to play at the same time. Disabled by default.                                                                                                                                    | `boolean`                                                                                                                             |
| `channel`                      | The active `StreamChat` channel instance loaded into `Channel` and referenced by its children. Do not provide this prop if you are using `ChannelList`, because `ChannelList` handles channel selection.        | `object`                                                                                                                              |
| `channelQueryOptions`          | Optional configuration for the initial channel query. Applied only if `channel.initialized` is `false`. If the channel instance is already initialized, the query is skipped and these options are not applied. | `ChannelQueryOptions`                                                                                                                 |
| `doDeleteMessageRequest`       | Custom action handler overriding the default `client.deleteMessage(message.id)` function.                                                                                                                       | `(message: LocalMessage, options?: DeleteMessageOptions) => Promise<MessageResponse>`                                                 |
| `doMarkReadRequest`            | Custom action handler overriding the default `channel.markRead` request. Receives the current channel and an optional unread-state updater.                                                                     | `(channel: StreamChannel, setChannelUnreadUiState?: (state: ChannelUnreadUiState) => void) => Promise<EventAPIResponse> \| void`      |
| `doSendMessageRequest`         | Custom action handler overriding the default `channel.sendMessage` request.                                                                                                                                     | `(channel: StreamChannel, message: Message, options?: SendMessageOptions) => Promise<SendMessageAPIResponse> \| void`                 |
| `doUpdateMessageRequest`       | Custom action handler overriding the default `client.updateMessage` request.                                                                                                                                    | `(cid: string, updatedMessage: LocalMessage \| MessageResponse, options?: UpdateMessageOptions) => Promise<UpdateMessageAPIResponse>` |
| `EmptyPlaceholder`             | Custom React element rendered when no active channel is set. By default, `Channel` renders the current `EmptyStateIndicator`. Pass `null` to suppress empty-state rendering entirely.                           | `ReactElement \| null`                                                                                                                |
| `giphyVersion`                 | The Giphy image version to render. See the keys of the [Image Object](https://developers.giphy.com/docs/api/schema#image-object) for supported values. Defaults to `fixed_height`.                              | `GiphyVersions`                                                                                                                       |
| `imageAttachmentSizeHandler`   | Custom function to provide size configuration for image attachments.                                                                                                                                            | `ImageAttachmentSizeHandler`                                                                                                          |
| `initializeOnMount`            | Prevents the initial `channel.watch()` call when mounting the component. When `false`, channel data is not fetched and WebSocket events are not subscribed until you initialize the channel yourself.           | `boolean`                                                                                                                             |
| `markReadOnMount`              | Controls whether the active channel is marked read when mounted. Defaults to `true`.                                                                                                                            | `boolean`                                                                                                                             |
| `onMentionsClick`              | Custom action handler to run when an `@mention` in a message is clicked. Receives the event, matched direct user if available, and the message metadata.                                                        | `OnMentionAction`                                                                                                                     |
| `onMentionsHover`              | Custom action handler to run when an `@mention` in a message is hovered. Receives the event, matched direct user if available, and the message metadata.                                                        | `OnMentionAction`                                                                                                                     |
| `shouldGenerateVideoThumbnail` | Turns video thumbnail generation on or off for video attachments.                                                                                                                                               | `boolean`                                                                                                                             |
| `skipMessageDataMemoization`   | If `true`, skips the message-data string comparison used to memoize current channel messages. This can help with channels that render thousands of messages.                                                    | `boolean`                                                                                                                             |
| `videoAttachmentSizeHandler`   | Custom function to provide size configuration for video attachments.                                                                                                                                            | `VideoAttachmentSizeHandler`                                                                                                          |

## Examples

### Create and pass a channel manually

```tsx
const channel = client.channel("messaging", {
  members: ["nate", "roy"],
});
```

### Provide custom initial query options

```tsx
import type { ChannelQueryOptions } from "stream-chat";
import { Channel, useChatContext } from "stream-chat-react";

const channelQueryOptions: ChannelQueryOptions = {
  messages: { limit: 20 },
  watchers: { limit: 10 },
};

type ChannelRendererProps = {
  id: string;
  type: string;
};

const ChannelRenderer = ({ id, type }: ChannelRendererProps) => {
  const { client } = useChatContext();

  return (
    <Channel
      channel={client.channel(type, id)}
      channelQueryOptions={channelQueryOptions}
    >
      {/* Channel children */}
    </Channel>
  );
};
```

### Customize delete-message requests

```tsx
import type {
  DeleteMessageOptions,
  LocalMessage,
  MessageResponse,
} from "stream-chat";
import { Channel } from "stream-chat-react";

const doDeleteMessageRequest = async (
  message: LocalMessage,
  options?: DeleteMessageOptions,
): Promise<MessageResponse> => {
  if (message.parent_id) {
    // custom reply deletion flow
  } else {
    // custom main-list deletion flow
  }

  return client.deleteMessage(message.id, options);
};

const App = () => (
  <Channel doDeleteMessageRequest={doDeleteMessageRequest}>{/* ... */}</Channel>
);
```

### Render a custom empty placeholder

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

const App = () => (
  <>
    <Channel EmptyPlaceholder={<div>Select a conversation</div>} />
    <Channel EmptyPlaceholder={null} />
  </>
);
```


---

This page was last updated at 2026-07-09T16:01:12.178Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/components/core-components/channel/](https://getstream.io/chat/docs/sdk/react/components/core-components/channel/).