Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

ChannelList

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 explains what the list renders and reads, and Channel lists covers the feature end to end.

Rendering a list

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

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. 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.
  • ChannelNavigation renders the list header, the search bar, 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.

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.

To handle the click yourself, pass onSelect to the row; see 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.

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 explains these states.

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 and 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 has an example, and Which channels a list shows 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:

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