# ChannelList

`ChannelList` queries channels from the Stream Chat API and renders them as a list. Use [`filters`](#filters), [`sort`](#sort), and [`options`](#options) to customize the underlying [Query Channels](/chat/docs/javascript/query-channels/) request.

```tsx
const channels = await client.queryChannels(filters, sort, options);
```

## Best Practices

- Always include a `members` filter to avoid querying the entire app.
- Keep `sort` and `options` consistent with your UX (recency vs. priority lists).
- Set `options.limit` explicitly when first-load size and pagination behavior matter.
- Use `ChannelList` for navigation state instead of managing active channels manually.
- Use `WithComponents` to replace SDK-owned UI such as `ChannelListUI` or `ChannelListItemUI` when layout changes go beyond styling.
- Customize event handlers when you need to enforce filtering rules on real-time updates.

`ChannelList` also manages navigation: clicking a list item sets the active channel and renders [`Channel`](/chat/docs/sdk/react/components/core-components/channel/).

## Basic Usage

`ChannelList` has no required props, but you should almost always pass [`filters`](#filters), [`sort`](#sort), and [`options`](#options).

<admonition type="warning">

Without filters, the query matches all channels in your app. That’s usually only useful in development.

**At a minimum, the filter should include `{members: { $in: [userID] }}` .**

</admonition>

```tsx
const filters = { members: { $in: [ 'jimmy', 'buffet' ] } }
const sort = { last_message_at: -1 };
const options = { limit: 10 }

<Chat client={client}>
  <ChannelList filters={filters} sort={sort} options={options} />
  <Channel>
    <MessageList />
    <MessageComposer />
  </Channel>
</Chat>
```

Set `options.limit` intentionally. The initial list size and subsequent pagination behavior depend on the query options you provide.

## UI Customization

`ChannelList` keeps data loading, event handling, and pagination on props. SDK-owned UI customization now flows through `WithComponents` / `ComponentContext`.

The default channel-list container is
[`ChannelListUI`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListUI.tsx)
and the default row UI is
[`ChannelListItemUI`](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelListItem/ChannelListItemUI.tsx).

If you enable `showChannelSearch`, `ChannelList` renders the [`Search`](/chat/docs/sdk/react/components/utility-components/channel-search/) UI above the list.
Search customization now happens through `WithComponents` / `ComponentContext`, not through `ChannelList` props.

To customize the container or row UI, override `ChannelListUI` or `ChannelListItemUI`. Use the same pattern for shared pieces such as `Avatar`, `LoadingIndicator`, and `LoadingErrorIndicator`.

```tsx
import {
  ChannelList,
  ChannelListItemUI,
  ChannelListUI,
  WithComponents,
} from "stream-chat-react";

const CustomChannelListUI = (props) => <ChannelListUI {...props} />;

const CustomChannelListItem = (props) => (
  <div className="custom-channel-list-item">
    <ChannelListItemUI {...props} />
  </div>
);

<WithComponents
  overrides={{
    ChannelListItemUI: CustomChannelListItem,
    ChannelListUI: CustomChannelListUI,
  }}
>
  <ChannelList />
</WithComponents>;
```

If `ChannelListItemUI` isn’t enough (for example, you need grouped sections), use `renderChannels`, which receives all loaded channels.

```tsx
const renderChannels = (loadedChannels, renderChannel) => {
  const groups = groupBy(loadedChannels, 'some_custom_channel_data');
  return renderGroups(groups); // inside renderGroups you have have headings, etc...
}

<Chat client={client}>
  <ChannelList {/* other props */} renderChannels={renderChannels} />
  <Channel>
    <MessageList />
    <MessageComposer />
  </Channel>
</Chat>;
```

## Event Listeners

`ChannelList` registers [event listeners](/chat/docs/javascript/event-object/) on mount. Many handlers can be overridden via props.

Each handler receives the same arguments; you can read the event data and update list state as needed.

- `setChannels` - state setter for the `channels` value which populates the list in the DOM
- `event` - event object returned from each corresponding event listener

| [Event Type](/chat/docs/javascript/event-object/) | Default Behavior                                 | Custom Handler                                |
| ------------------------------------------------- | ------------------------------------------------ | --------------------------------------------- |
| `channel.deleted`                                 | Removes channel from list                        | [onChannelDeleted](#onchanneldeleted)         |
| `channel.hidden`                                  | Removes channel from list                        | [onChannelHidden](#onchannelhidden)           |
| `channel.truncated`                               | Updates the channel                              | [onChannelTruncated](#onchanneltruncated)     |
| `channel.updated`                                 | Updates the channel                              | [onChannelUpdated](#onchannelupdated)         |
| `channel.visible`                                 | Adds channel to list                             | [onChannelVisible](#onchannelvisible)         |
| `connection.recovered`                            | Forces a component render                        | N/A                                           |
| `message.new`                                     | Moves channel to top of list                     | [onMessageNewHandler](#onmessagenewhandler)   |
| `notification.added_to_channel`                   | Moves channel to top of list and starts watching | [onAddedToChannel](#onaddedtochannel)         |
| `notification.message_new`                        | Moves channel to top of list and starts watching | [onMessageNew](#onmessagenew)                 |
| `notification.removed_from_channel`               | Removes channel from list                        | [onRemovedFromChannel](#onremovedfromchannel) |
| `user.presence.changed`                           | Updates the channel                              | N/A                                           |

## Customizing Event Handlers

You can override the behavior for each event. Here’s an example that keeps the list limited to [frozen channels](/chat/docs/javascript/disabling-channels/).

```tsx
const filters = {
  members: { $in: ['dan'] },
  frozen: true
}

<ChannelList filters={filters} />
```

The `notification.message_new` event occurs when a message is received on a channel that is not loaded but the current user is a member of.
By default, this event queries the channel and adds it to the top of the list, regardless of `filters`.
Thus, if a new message appears in an unfrozen channel of which the current user is a member, it will be added to the list. This may not be the desired behavior since the list is only supposed to show frozen channels.

Provide a custom `onMessageNew` prop to override this. It receives `setChannels` and the `event` so you can enforce your own filtering.

```tsx
const filters = {
  members: { $in: ["dan"] },
  frozen: true,
};

const customOnMessageNew = async (setChannels, event) => {
  const eventChannel = event.channel;

  // If the channel isn't frozen, then don't add it to the list.
  if (!eventChannel?.id || !eventChannel.frozen) return;

  try {
    const newChannel = client.channel(eventChannel.type, eventChannel.id);
    await newChannel.watch();
    setChannels((channels) => [newChannel, ...channels]);
  } catch (error) {
    console.log(error);
  }
};

<ChannelList filters={filters} onMessageNew={customOnMessageNew} />;
```

Other events can be overridden similarly.

## Props

| Prop                                     | Description                                                                                                                                                                                                                                                                                                                                                       | Type                                                                                                                                           |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowNewMessagesFromUnfilteredChannels` | When the client receives `message.new`, `notification.message_new`, and `notification.added_to_channel` events, the SDK automatically pushes that channel to the top of the list. If the channel does not currently exist in the list, it is taken from `client.activeChannels` and added. Set this prop to `false` to disable that behavior. Defaults to `true`. | `boolean`                                                                                                                                      |
| `channelRenderFilterFn`                  | Optional function to filter channels before loading them in the DOM. Avoid complex or async logic that delays `ChannelList`; prefer a pure function with array methods like `filter`, `sort`, or `reduce`.                                                                                                                                                        | `(channels: Channel[]) => Channel[]`                                                                                                           |
| `customActiveChannel`                    | Sets a channel with this ID as active and forces it to the top of the list.                                                                                                                                                                                                                                                                                       | `string`                                                                                                                                       |
| `customQueryChannels`                    | Custom function that handles channel pagination. It receives `currentChannels`, `queryType`, `setChannels`, and `setHasNextPage`, and is responsible for building query filters/sort/options, appending results, and updating pagination state.                                                                                                                   | `[CustomQueryChannelsFn](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/hooks/usePaginatedChannels.ts)` |
| `EmptyStateIndicator`                    | Custom UI component for rendering an empty list. Defaults to [EmptyStateIndicator](https://github.com/GetStream/stream-chat-react/blob/master/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx).                                                                                                                                                        | `component`                                                                                                                                    |
| `filters`                                | Object containing channel query filters. See the [query parameters docs](/chat/docs/javascript/query-channels/#query-parameters/) for more information.                                                                                                                                                                                                           | `object`                                                                                                                                       |
| `getLatestMessagePreview`                | Custom function that generates the message preview in `ChannelListItem`.                                                                                                                                                                                                                                                                                          | `(channel: Channel, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => string \| JSX.Element`          |
| `lockChannelOrder`                       | When `true`, channels do not dynamically resort by most recent message. Defaults to `false`.                                                                                                                                                                                                                                                                      | `boolean`                                                                                                                                      |
| `onAddedToChannel`                       | Function overriding the default behavior when a user is added to a channel.                                                                                                                                                                                                                                                                                       | `function`                                                                                                                                     |
| `onChannelDeleted`                       | Function overriding the default behavior when a channel is deleted.                                                                                                                                                                                                                                                                                               | `function`                                                                                                                                     |
| `onChannelHidden`                        | Function overriding the default behavior when a channel is hidden.                                                                                                                                                                                                                                                                                                | `function`                                                                                                                                     |
| `onChannelTruncated`                     | Function overriding the default behavior when a channel is truncated.                                                                                                                                                                                                                                                                                             | `function`                                                                                                                                     |
| `onChannelUpdated`                       | Function overriding the default behavior when a channel is updated.                                                                                                                                                                                                                                                                                               | `function`                                                                                                                                     |
| `onChannelVisible`                       | Function overriding the default behavior when a channel becomes visible.                                                                                                                                                                                                                                                                                          | `function`                                                                                                                                     |
| `onMessageNew`                           | Function overriding the default behavior when a message is received on a channel that is not being watched.                                                                                                                                                                                                                                                       | `function`                                                                                                                                     |
| `onMessageNewHandler`                    | Function overriding the default behavior when a message is received on a watched channel. Handles the `message.new` event.                                                                                                                                                                                                                                        | `(setChannels: React.Dispatch<React.SetStateAction<Array<Channel>>>, event: Event) => void`                                                    |
| `onRemovedFromChannel`                   | Function overriding the default behavior when a user is removed from a channel.                                                                                                                                                                                                                                                                                   | `function`                                                                                                                                     |
| `options`                                | Object containing channel query options. See the [query parameters docs](/chat/docs/javascript/query-channels/#query-parameters/) for more information.                                                                                                                                                                                                           | `object`                                                                                                                                       |
| `Paginator`                              | Custom UI component that handles channel pagination logic. Defaults to [LoadMorePaginator](https://github.com/GetStream/stream-chat-react/blob/master/src/components/LoadMore/LoadMorePaginator.tsx).                                                                                                                                                             | `component`                                                                                                                                    |
| `recoveryThrottleIntervalMs`             | Interval during which recovery channel-list queries are prevented to avoid unnecessary reloads during connection fluctuation. Recovery reloads the list from offset `0`. The minimum interval is `2000ms`. Defaults to `5000ms`. This applies only if the `StreamChat` client's built-in channel-list recovery is disabled.                                       | `number`                                                                                                                                       |
| `renderChannels`                         | Function overriding the default render behavior so it is called instead of rendering `ChannelListItemUI` directly.                                                                                                                                                                                                                                                | `function`                                                                                                                                     |
| `sendChannelsToList`                     | If `true`, sends the list's currently loaded channels to the `ChannelListUI` component as the `loadedChannels` prop. Defaults to `false`.                                                                                                                                                                                                                         | `boolean`                                                                                                                                      |
| `setActiveChannelOnMount`                | If `true`, sets the most recent channel returned by the query as active on mount. If `false`, no channel is set active on mount. Defaults to `true`.                                                                                                                                                                                                              | `boolean`                                                                                                                                      |
| `showChannelSearch`                      | If `true`, renders the [`Search`](/chat/docs/sdk/react/components/utility-components/channel-search/) UI above the channel-list UI. Customize the search surface through `WithComponents` / `ComponentContext`. Defaults to `false`.                                                                                                                              | `boolean`                                                                                                                                      |
| `sort`                                   | Object containing channel query sort parameters. See the [query parameters docs](/chat/docs/javascript/query-channels/#query-parameters/) for more information.                                                                                                                                                                                                   | `object`                                                                                                                                       |
| `watchers`                               | Object containing query parameters for fetching channel watchers.                                                                                                                                                                                                                                                                                                 | `{ limit?: number; offset?: number }`                                                                                                          |

## Examples

### Custom channel querying

`customQueryChannels` receives:

| Parameter         | Description                                                                           |
| ----------------- | ------------------------------------------------------------------------------------- |
| `currentChannels` | The state of loaded `Channel` objects queried so far.                                 |
| `queryType`       | Whether the state should reset to the first page (`"reload"`) or append new channels. |
| `setChannels`     | Function that updates the channels state reflected in `currentChannels`.              |
| `setHasNextPage`  | Flag setter indicating whether more items can be loaded from the API.                 |

It is responsible for:

1. building or providing query filters, sort, and options
2. querying and appending channels to the current channels state
3. updating the `hasNext` pagination flag after each query

The example below implements a custom query function that uses different filters sequentially once a preceding filter is exhausted:

```ts
import uniqBy from "lodash.uniqby";
import throttle from "lodash.throttle";
import { useCallback, useRef } from "react";
import {
  ChannelFilters,
  ChannelOptions,
  ChannelSort,
  StreamChat,
} from "stream-chat";
import { CustomQueryChannelParams, useChatContext } from "stream-chat-react";

const DEFAULT_PAGE_SIZE = 30 as const;

export const useCustomQueryChannels = () => {
  const { client } = useChatContext();
  const filters1: ChannelFilters = {
    member_count: { $gt: 10 },
    members: { $in: [client.user?.id || ""] },
    type: "messaging",
  };
  const filters2: ChannelFilters = {
    members: { $in: [client.user?.id || ""] },
    type: "messaging",
  };
  const options: ChannelOptions = { limit: 10, presence: true, state: true };
  const sort: ChannelSort = { last_message_at: -1, updated_at: -1 };

  const filtersArray = [filters1, filters2];
  const appliedFilterIndex = useRef(0);

  const customQueryChannels = useCallback(
    throttle(
      async ({
        currentChannels,
        queryType,
        setChannels,
        setHasNextPage,
      }: CustomQueryChannelParams) => {
        const offset = queryType === "reload" ? 0 : currentChannels.length;

        const newOptions = {
          limit: options.limit ?? DEFAULT_PAGE_SIZE,
          offset,
          ...options,
        };

        const filters = filtersArray[appliedFilterIndex.current];
        const channelQueryResponse = await client.queryChannels(
          filters,
          sort || {},
          newOptions,
        );

        const newChannels =
          queryType === "reload"
            ? channelQueryResponse
            : uniqBy([...currentChannels, ...channelQueryResponse], "cid");

        setChannels(newChannels);

        const lastPageForCurrentFilter =
          channelQueryResponse.length < newOptions.limit;
        const isLastPageForAllFilters =
          lastPageForCurrentFilter &&
          appliedFilterIndex.current === filtersArray.length - 1;

        setHasNextPage(!isLastPageForAllFilters);
        if (lastPageForCurrentFilter) {
          appliedFilterIndex.current += 1;
        }
      },
      500,
      { leading: true, trailing: false },
    ),
    [client, filtersArray],
  );

  return customQueryChannels;
};
```

It is recommended to control for duplicate requests by throttling the custom function calls.

### Configure recovery throttling

The channel-list recovery mechanism described here is activated only if the `StreamChat` client's built-in channel-list recovery is disabled:

```typescript jsx
import { StreamChat } from 'stream-chat';
import { ChannelList, Chat } from 'stream-chat-react';

// ... get apiKey, filters, sort, options

const client = new StreamChat(apiKey, {recoverStateOnReconnect: false});
const App = () => (
    <Chat client={client} >
     {/** ... */}
        <ChannelList
        filters={filters}
        sort={sort}
        options={options}
        recoveryThrottleIntervalMs={3000}
        {/** other props... */}
      />
     {/** ... */}
    </Chat>
);

```


---

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

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