# ChannelList

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

`ChannelList` renders the channels of one `ChannelPaginator`, a row per channel, and loads the next page as the user scrolls. The paginator holds the query and the loaded channels; the list only renders them. [Channel list UI](https://getstream.io/chat/docs/sdk/react/v15/ui/components/channel-list-ui/) explains what the list renders and reads, and [Channel lists](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-lists/) covers the feature end to end.

## Rendering a list

Create a paginator, register it on `client.channelManager`, and pass it to `ChannelList`:

```tsx
import { useEffect, useMemo } from "react";
import { ChannelPaginator } from "stream-chat";
import type { ChannelFilters, SortParamRequest } from "stream-chat";
import { ChannelList, useChatContext } from "stream-chat-react";

const sort: SortParamRequest[] = [{ direction: -1, field: "last_message_at" }];

export const Inbox = ({ userId }: { userId: string }) => {
  const { client } = useChatContext();

  const paginator = useMemo(() => {
    const filters: ChannelFilters = { members: { $in: [userId] } };
    return new ChannelPaginator({ client, filters, id: "inbox", sort });
  }, [client, userId]);

  // A registered list is kept up to date as events arrive.
  useEffect(() => {
    client.channelManager.insertPaginator({ paginator });
    return () => {
      client.channelManager.removePaginator(paginator);
    };
  }, [client, paginator]);

  return <ChannelList paginator={paginator} />;
};
```

Filters, sort and registering are explained on [Channel list paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/channel-list-paginators/). Scope the filters to the user, as above: without that, a list can show channels the user isn't a member of.

Two components render every registered list for you. Neither takes props:

- `ChannelLists` renders a `ChannelList` for each paginator on `client.channelManager`, in their order, and provides [`ChannelListContext`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-list/channellistcontext/).
- `ChannelNavigation` renders the list header, the [search bar](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-list/search-components/), `ChannelLists` and the notifications of the `channel-list` panel. While a search is open, the results take the lists' place.

Outside `ChatView`, the list brings the `str-chat` element the design tokens are declared on. `ChannelNavigation`'s root gets the `str-chat` and `theme` classes, and a `ChannelList` rendered on its own is wrapped in an element with them that has `display: contents`. Inside `ChatView`, neither adds one, and the list uses `ChatView`'s. See [Design tokens](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/theming/#design-tokens).

## Selecting a channel

The list holds no selection. Clicking a row calls `openChannel(channel, { event })` from `useWorkspaceNavigation()`, and the layout decides where the channel opens. A row is highlighted while `isChannelActive(cid)` is true for its channel. Without a layout, clicking a row does nothing; see [Navigation](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/navigation/#without-a-layout).

To handle the click yourself, pass `onSelect` to the row; see [Handle the row click yourself](#handle-the-row-click-yourself).

## UI customization

Register replacements with `WithComponents`. `ChannelList` reads these keys:

| Override key                | Replaces                                                     | Gets                                          |
| --------------------------- | ------------------------------------------------------------ | --------------------------------------------- |
| `ListItem`                  | The whole row. The default renders `ChannelListItem`.        | `item`: the channel                           |
| `FirstPageLoadingIndicator` | The placeholder rows while the first page loads              | nothing                                       |
| `LoadingNextPageIndicator`  | The spinner after the last row while the next page loads     | `isLoading`                                   |
| `EmptyListIndicator`        | "No conversations yet", when the list has no channels        | nothing                                       |
| `EndReachedIndicator`       | Nothing by default. Renders at the start and end of the list | `hasEnded`, `reached` (`"top"` or `"bottom"`) |

The default row reads `ChannelListItemUI`, `Avatar`, `SummarizedMessagePreview` and `ChannelListItemActionButtons`; see [Channel Preview](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-list/channel-preview/).

`EmptyStateIndicator` and `LoadingIndicator` don't change the list: it renders its own empty state and spinner, and has no error state. When a query fails, the list publishes an error notification. [Channel list UI](https://getstream.io/chat/docs/sdk/react/v15/ui/components/channel-list-ui/#loading-empty-and-error-states) explains these states.

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

const NoChannels = () => <p className="no-channels">No conversations yet.</p>;

const overrides = { EmptyListIndicator: NoChannels };

export const ListWithEmptyState = () => (
  <WithComponents overrides={overrides}>
    <ChannelList paginator={paginator} />
  </WithComponents>
);
```

## Props

| Prop                  | Type               | Default  | Description                                                                                            |
| --------------------- | ------------------ | -------- | ------------------------------------------------------------------------------------------------------ |
| `paginator`           | `ChannelPaginator` | required | The paginator whose channels the list renders. Register it on `client.channelManager`.                 |
| `loadMoreDebounceMs`  | `number`           | `500`    | How long the list waits after the user stops scrolling before it checks whether to load the next page. |
| `loadMoreThresholdPx` | `number`           | `250`    | How close to the end of the list, in pixels, scrolling has to come to load the next page.              |

The two load-more props trade responsiveness for work. A larger threshold starts the next page before the user reaches the end, so a fast scroll hits a spinner less often, but it loads pages the user may never see. A shorter debounce reacts sooner after the user stops scrolling, but checks the scroll position more often.

On mount, the list loads the first page if the paginator hasn't loaded one yet, and again whenever the paginator is reset to that state, as `client.disconnectUser()` does. It also starts the channel manager's event subscriptions, which every list shares.

Everything else about a list is set on its paginator and on `client.channelManager`: filters, sort, page size, and how events move channels between lists. See [Channel list paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/channel-list-paginators/) and [Channel manager and ChannelPaginator](https://getstream.io/chat/docs/sdk/react/v15/reference/state/channel-manager-and-channelpaginator/).

## Examples

### Several lists

Register one paginator per list. `ChannelNavigation` and `ChannelLists` render them in order; to place them yourself, render one `ChannelList` per paginator. [Several lists](https://getstream.io/chat/docs/sdk/react/v15/ui/components/channel-list-ui/#several-lists) has an example, and [Which channels a list shows](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-lists/#which-channels-a-list-shows-and-in-what-order) an inbox with an archive.

### Handle the row click yourself

Register a `ListItem` that renders `ChannelListItem` with `onSelect`. The row then calls it instead of opening the channel:

```tsx
import type { Channel } from "stream-chat";
import { ChannelListItem, WithComponents } from "stream-chat-react";
import type { PropsWithChildren } from "react";

const RowThatLogs = ({ item }: { item: unknown }) => {
  const channel = item as Channel;
  return (
    <ChannelListItem
      channel={channel}
      onSelect={() => console.log("Selected", channel.cid)}
    />
  );
};

const overrides = { ListItem: RowThatLogs };

export const WithLoggingRows = ({ children }: PropsWithChildren) => (
  <WithComponents overrides={overrides}>{children}</WithComponents>
);
```

## Hooks

- [`useChannelPaginatorState`](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/loading-data-hooks/#usechannelpaginatorstate): the loaded channels of a paginator, and whether more are loading.
- [`useChannelListContext`](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/loading-data-hooks/#usechannellistcontext): the paginator `ChannelLists` provides.
- [`useWorkspaceNavigation`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/layout/workspace-navigation-api/): `openChannel` and `isChannelActive`.

---

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