# ChannelListContext

The context value is provided by `ChannelListContextProvider`, which wraps the content rendered by [`ChannelList`](/chat/docs/sdk/react/components/core-components/channel-list/).
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:

```tsx
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`](/chat/docs/sdk/react/components/core-components/channel-list/).                                                                             | `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:

```tsx
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>
  );
};
```


---

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

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