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:
ChannelListsrenders aChannelListfor each paginator onclient.channelManager, in their order, and providesChannelListContext.ChannelNavigationrenders the list header, the search bar,ChannelListsand the notifications of thechannel-listpanel. 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
useChannelPaginatorState: the loaded channels of a paginator, and whether more are loading.useChannelListContext: the paginatorChannelListsprovides.useWorkspaceNavigation:openChannelandisChannelActive.