Channel Header

This example shows how to use a custom ChannelHeader. Unlike most components that are overridden via props, you can just render your custom header in place of the default.

Best Practices

  • Keep header UI lightweight to avoid re-render overhead.
  • Pull channel data from ChannelStateContext via useChannelStateContext instead of duplicating state.
  • Use TypingIndicator thoughtfully to avoid noisy headers.
  • Remove the default typing indicator if you render a custom one.
  • Preserve accessibility for channel title and controls.

Implementation

The default header displays the channel name, member count, and online count. In this example, we render the name and add a typing indicator below. Because the custom header is a child of Channel, it can access channel via ChannelStateContext and useChannelStateContext.

const CustomChannelHeader = (props: ChannelHeaderProps) => {
  const { title } = props;

  const { channel } = useChannelStateContext();
  const { name } = channel.data || {};

  return <div>{title || name}</div>;
};

Next, add the typing indicator to the header and remove the default one in MessageList by overriding it with a null component.

const CustomChannelHeader = (props: ChannelHeaderProps) => {
    const { title } = props;

    const { channel } = useChannelStateContext();
    const { name } = channel.data || {};

    return (
        <>
            <div>{title || name}</div>
            <TypingIndicator />
        </>
    )
}

<Channel TypingIndicator={() => null}>

You can render additional header details by pulling more data from channel. For this demo, we’ll add some basic styling.

.header-title {
  padding: 5px 7px;
}

.header-pound {
  color: #006cff;
  font-weight: 800;
  padding-right: 2px;
}
const CustomChannelHeader = (props: ChannelHeaderProps) => {
  const { title } = props;

  const { channel } = useChannelStateContext();
  const { name } = channel.data || {};

  return (
    <div className="str-chat__header-livestream">
      <div>
        <div className="header-item">
          <span className="header-pound">#</span>
          {title || name}
        </div>
        <TypingIndicator />
      </div>
    </div>
  );
};

The Final Code

Finally, render the custom header where the default would normally appear.

.header-title {
  padding: 5px 7px;
}

.header-pound {
  color: #006cff;
  font-weight: 800;
  padding-right: 2px;
}
const CustomChannelHeader = (props: ChannelHeaderProps) => {
  const { title } = props;

  const { channel } = useChannelStateContext();
  const { name } = channel.data || {};

  return (
    <div className="str-chat__header-livestream">
      <div>
        <div className="header-item">
          <span className="header-pound">#</span>
          {title || name}
        </div>
        <TypingIndicator />
      </div>
    </div>
  );
};
const App = () => (
  <Chat client={chatClient}>
    <ChannelList />
    <Channel TypingIndicator={() => null}>
      <Window>
        <CustomChannelHeader />
        <MessageList />
        <MessageInput />
      </Window>
      <Thread />
    </Channel>
  </Chat>
);

The Result

Custom ChannelHeader in Chat