Info:
This is documentation for Stream Chat React SDK v14, which is no longer actively maintained. For up-to-date documentation, see the latest version (v15).

ChannelListContext

The context value is provided by ChannelListContextProvider, which wraps the content rendered by ChannelList. It exposes API used by the default and custom components. In v14, SDK-owned channel list UI customization mainly flows through WithComponents / ComponentContext:

  • ChannelListUI - renders the list container, loading state, and error state
  • ChannelListItemUI - renders the information for one channel row
  • Avatar - component used to display the channel image inside the default row UI
  • LoadingErrorIndicator - rendered when the channels query fails
  • LoadingIndicator - rendered during the channels query
  • Search - renders the search input and results when showChannelSearch is enabled
  • EmptyStateIndicator - still provided directly through ChannelList
  • Paginator - still provided directly through ChannelList

Best Practices

  • Read from context instead of passing channel list state down manually.
  • Defer setChannels until the initial query completes to avoid overwrites.
  • Keep custom list components aligned with the default loading/error states.
  • Use Paginator for infinite scroll instead of DIY pagination.
  • Register SDK-owned channel list UI overrides through WithComponents.
  • Gate expensive logic to avoid blocking list rendering.

Basic Usage

Access the API from ChannelListContext with our custom hook:

import { useChannelListContext } from "stream-chat-react";

export const CustomComponent = () => {
  const { channels, setChannels } = useChannelListContext();
  // component logic ...
  return {/* rendered elements */};
};

Values

Value Description Type
channels State representing the array of loaded channels. By default, the channels query is executed by ChannelList. Channel[]
setChannels Sets the list of channels rendered by ChannelList. Be careful when calling this early: the initial ChannelList query overwrites the whole channels state. A common pattern is to wait for the channels.queried event before calling setChannels. Dispatch<SetStateAction<Channel[]>>

Examples

Set the active channel from the URL

The example below waits for the first page, then falls back to getChannel() if the channel is not in that page:

import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import type { Event } from 'stream-chat';
import {
  ChannelList,
  ChannelListUI,
  type ChannelListUIProps,
  WithComponents,
  getChannel,
  useChannelListContext,
  useChatContext,
} from 'stream-chat-react';

const DEFAULT_CHANNEL_ID = 'general';
const DEFAULT_CHANNEL_TYPE = 'messaging';

const CustomChannelListUI = (props: ChannelListUIProps) => {
  const { channelId } = useParams();
  const navigate = useNavigate();
  const { client, channel, setActiveChannel } = useChatContext();
  const { setChannels } = useChannelListContext();

  useEffect(() => {
    if (!channelId) return navigate(`/${DEFAULT_CHANNEL_ID}`);

    if (channel?.id === channelId || !client) return;

    let subscription: { unsubscribe: () => void } | undefined;
    if(!channel?.id || channel?.id !== channelId) {
      subscription = client.on('channels.queried', (event: Event) => {
        const loadedChannelData = event.queriedChannels?.channels.find((response) => response.channel.id === channelId);

        if (loadedChannelData) {
          setActiveChannel(client.channel( DEFAULT_CHANNEL_TYPE, channelId));
          subscription?.unsubscribe();
          return;
        }

        return getChannel({client, id: channelId, type: DEFAULT_CHANNEL_TYPE}).then((newActiveChannel) => {
          setActiveChannel(newActiveChannel);
          setChannels((channels) => {
            return ([newActiveChannel, ...channels.filter((ch) => ch.data?.cid !== newActiveChannel.data?.cid)]);
          });
        });
      });
    }

    return () => {
      subscription?.unsubscribe();
    };
  }, [channel?.id, channelId, setChannels, client, navigate, setActiveChannel]);

  return <ChannelListUI {...props} />;
};

const Sidebar = () => {
  return (
    <WithComponents overrides={{ ChannelListUI: CustomChannelListUI }}>
      <ChannelList
        {/* some props */}
        {/* setting active channel will be performed inside the custom ChannelListUI component */}
        setActiveChannelOnMount={false}
        {/* some props */}
      />
    </WithComponents>
  );
};