# Channel Header

This example shows how to render a custom `ChannelHeader`.

Unlike SDK-owned UI surfaces that are replaced through `WithComponents`, a custom channel header is usually just rendered in place of the default `ChannelHeader`.

## Best Practices

- Pull channel display data from hooks such as `useChannelPreviewInfo()` instead of duplicating it.
- Use `TypingIndicatorHeader` for header subtitle typing state, not the message-list `TypingIndicator`.
- Keep the header layout light because it rerenders with channel and typing updates.
- Preserve a sidebar toggle if your layout depends on `ChannelList` on smaller screens.
- Prefer your own custom class names over older SDK-specific header class names.

## Implementation

The default header derives the title and image from the current channel and swaps its subtitle between typing state and online/member status. A custom header can reuse the same data:

```tsx
import {
  ChannelAvatar,
  TypingIndicatorHeader,
  useChannelPreviewInfo,
  useChannelStateContext,
  useTypingContext,
} from "stream-chat-react";

const CustomChannelHeader = () => {
  const { channel, channelConfig } = useChannelStateContext();
  const { typing = {} } = useTypingContext();
  const { displayImage, displayTitle, groupChannelDisplayInfo } =
    useChannelPreviewInfo({ channel });

  const hasTyping =
    channelConfig?.typing_events !== false &&
    Object.values(typing).some(({ parent_id }) => !parent_id);

  return (
    <div className="custom-channel-header">
      <ChannelAvatar
        displayMembers={groupChannelDisplayInfo.members}
        imageUrl={displayImage}
        size="lg"
        userName={displayTitle}
      />
      <div className="custom-channel-header__content">
        <div className="custom-channel-header__title">{displayTitle}</div>
        <div className="custom-channel-header__subtitle">
          {hasTyping ? <TypingIndicatorHeader /> : "Custom subtitle"}
        </div>
      </div>
    </div>
  );
};
```

`ChannelAvatar` computes the visible members and overflow badge from `displayMembers`, so you do not need to pass a separate overflow value.

```css
.custom-channel-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
}

.custom-channel-header__content {
  min-width: 0;
}

.custom-channel-header__title {
  font-weight: 600;
}

.custom-channel-header__subtitle {
  color: #6b7280;
  font-size: 0.875rem;
}
```

## Final Code

Render your custom header where you would normally place `ChannelHeader`:

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

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


---

This page was last updated at 2026-05-22T16:32:13.881Z.

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