import { StreamChat } from 'stream-chat';
import { ChannelList, Chat, OverlayProvider } from 'stream-chat-react-native';
const client = StreamChat.getInstance('api_key');
const filters = { members: { $in: [ 'vishal', 'lucas', 'neil' ] } };
const sort = { last_updated: -1 };
const options = { limit: 20, messages_limit: 30 };
export const App = () => <OverlayProvider>
<Chat client={client}>
<ChannelList
filters={filters}
sort={sort}
options={options}
onSelect={(channel) => /** navigate to channel screen */ }
/>
</Chat>
</OverlayProvider>;ChannelList
ChannelList displays channels using React Native's FlatList.
ChannelList fetches channels via the client's query channels function. Pass filters, sort, and options via props.
Use onSelect to handle navigation when a user taps a channel.
Best Practices
- Memoize
filtersandsortto avoid unnecessary re-queries. - Keep
onSelectlightweight and handle navigation outside of render. - Handle
notification.message_new/message.newif you need strict filtering on real-time updates. - Avoid passing
Channelinstances through navigation params; store them in state instead. - Use sensible
loadMoreThresholdvalues to reduce extra queries.
General Usage
ChannelList renders channels in a FlatList.
Note: Define the component within
OverlayProviderandChatso the required contexts are available.
Complete navigation example
Here's a practical example showing how to integrate ChannelList with React Navigation:
import React, { useMemo, useState, useEffect } from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import {
Chat,
OverlayProvider,
ChannelList,
Channel,
MessageList,
MessageComposer,
useCreateChatClient,
useChatContext,
} from "stream-chat-react-native";
const Stack = createStackNavigator();
const ChannelListScreen = ({ navigation }) => {
// Memoize filters and sort to prevent unnecessary re-renders
const filters = useMemo(
() => ({ members: { $in: ["user-id"] }, type: "messaging" }),
[],
);
const sort = useMemo(() => [{ last_message_at: -1 }], []);
return (
<ChannelList
filters={filters}
sort={sort}
onSelect={(channel) => {
// Navigate to the channel screen
// Note: Passing the channel CID, not the channel object
navigation.navigate("Channel", { channelId: channel.cid });
}}
/>
);
};
const ChannelScreen = ({ route }) => {
const { channelId } = route.params;
const [channel, setChannel] = useState();
const { client } = useChatContext();
useEffect(() => {
// Extract channel type and ID from CID (format: "type:id")
const [type, id] = channelId.split(":");
const channelInstance = client.channel(type, id);
setChannel(channelInstance);
}, [channelId, client]);
if (!channel) return null;
return (
<Channel channel={channel}>
<MessageList />
<MessageComposer />
</Channel>
);
};
export const App = () => {
const chatClient = useCreateChatClient({
apiKey: "YOUR_API_KEY",
userData: { id: "user-id", name: "User Name" },
tokenOrProvider: "YOUR_TOKEN",
});
if (!chatClient) return null;
return (
<OverlayProvider>
<Chat client={chatClient}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Channels" component={ChannelListScreen} />
<Stack.Screen name="Channel" component={ChannelScreen} />
</Stack.Navigator>
</NavigationContainer>
</Chat>
</OverlayProvider>
);
};When using navigation, pass the channel CID (e.g., "messaging:channel-id") instead of the channel object, since Channel instances are not serializable and will cause React Navigation warnings.
When channels are added via events, filters are not applied (filters can be complex and aren't re-evaluated client-side). If you need strict filtering, override notification.message_new via onNewMessageNotification and message.new via onNewMessage.
Context Providers
ChannelList provides ChannelsContext, accessible via its hook.
ChannelsContext(viauseChannelsContext) - provides channel list state and actions
Props
| Prop | Description | Type |
|---|---|---|
filters | Filter object passed to query channels. You can filter on built-in and custom fields. For best performance, pass a stable reference (not created inline), or memoize it. | object |
sort | Sort object passed to query channels. You can sort on built-in and custom fields. For best performance, pass a stable reference (not created inline), or memoize it. | object |
options | Options object passed internally to the client query function as a parameter. Unlike filters or sort, changing the options object alone will not re-query the list of channels. | object |
channelRenderFilterFn | Optional function to filter channels prior to rendering the list. Do not use any complex logic that would delay the loading of the ChannelList. We recommend using a pure function with array methods like filter/sort/reduce. | (channels: Channel[]) => Channel[] |
onSelect | Called when a user taps a channel. Receives the Channel instance for that item and is often used for navigation. Channel instances are not serializable, so passing them through navigation params will warn. | (channel) => void |
additionalFlatListProps | Additional props provided to the underlying FlatList. | object |
loadMoreThreshold | Sets the onEndReachedThreshold. We recommend 0.1; higher values may trigger extra channelQuery calls. Defaults to 0.1. | number |
lockChannelOrder | Locks the order of the channels in the list so they will not dynamically reorder by most recent message when a new message is received. Defaults to false. | boolean |
maxUnreadCount | Max unread badge value. Cannot exceed 255 (backend limit). Defaults to 255. | number |
numberOfSkeletons | The number of Skeleton items to display in the LoadingIndicator. Defaults to 6. | number |
onAddedToChannel | Override the default handler for when the user is added to a channel (default adds the channel). Receives parameters: setChannels (setter for internal channels state), event (Event Object for notification.added_to_channel), options (ChannelListEventListenerOptions object). | function |
onChannelDeleted | Override the default handler for channel deletion (default removes the channel). Receives parameters: setChannels (setter for internal channels state), event (Event Object for channel.deleted). | function |
onChannelHidden | Override the default handler for channel hidden (default removes the channel). Receives parameters: setChannels (setter for internal channels state), event (Event Object for channel.hidden). | function |
onChannelMemberUpdated | Override the default handler for member.updated. Required for pinning and archiving to work. Receives parameters: lockChannelOrder, setChannels, event (Event Object for member.updated), options (ChannelListEventListenerOptions object). | function |
onChannelVisible | Override the default handler for channel visible (default adds the channel). Receives parameters: setChannels (setter for internal channels state), event (Event Object for channel.visible). | function |
onChannelTruncated | Override the default handler for channel truncated (default reloads the list). Receives parameters: setChannels (setter for internal channels state), event (Event Object for channel.truncated). | function |
onChannelUpdated | Override the default handler for channel updated (default updates the channel data from the event). Receives parameters: setChannels (setter for internal channels state), event (Event Object for channel.updated). | function |
onNewMessageNotification | Override the default handler for new message on an unwatched channel (default adds the channel). Receives parameters: setChannels (setter for internal channels state), event (Event Object for notification.message_new), options (ChannelListEventListenerOptions object). | function |
onNewMessage | Override the default handler for new message on a watched channel (default moves the channel to the top). Receives parameters: lockChannelOrder, setChannels, event (Event Object for message.new), options (ChannelListEventListenerOptions object). | function |
onRemovedFromChannel | Override the default handler for when the user is removed from a channel (default removes the channel). Receives parameters: setChannels (setter for internal channels state), event (Event Object for notification.removed_from_channel). | function |
setFlatlistRef | Callback function to access the underlying FlatList ref. | (ref) => void |
mutedStatusPosition | Position of the muted status component within the ChannelPreview. Defaults to inlineTitle. | trailingBottom | inlineTitle |
swipeActionsEnabled | If true, the user can swipe to perform actions on a channel. Defaults to true. | boolean |
queryChannelsOverride | A function that overrides the ChannelManager queryChannels method (StreamChat.queryChannels). Use it to query specific cids while still paginating. StreamChat.queryChannels must be called inside, the return type must be Channel[], and limit/offset must be respected for pagination. Available since version 8.2.0. filters and sort should still be stable/memoized. See example below. | QueryChannelsRequestType |
Examples
queryChannelsOverride example
const cids = Array.from({ length: 25 }, (_, i) => (i + 1).toString());
const queryChannelsOverride = (filters, sort, options, ...restParams) => {
const { limit, offset } = options;
const cidWindow = cids.slice(offset, offset + limit);
const newFilters = { ...filters, cid: { $in: cidWindow } };
return chatClient.queryChannels(newFilters, sort, options, ...restParams);
};Given this ChannelList configuration:
// ... rest of the component
const filters = useMemo(() => ({}), []);
const options = useMemo(() => ({ offset: 0, limit: 10 }));
return <ChannelList filters={stableFilters} options={options} />;Pagination works as follows:
- On the first page, 10 items will be loaded, which will be the first 10
cids - On the second page,
queryChannelsOverridewill be invoked withoffset: 10andlimit: 10, socids10through19will be loaded - On the last page,
queryChannelsOverridewill be invoked withoffset: 10andlimit: 10, and so our implementation will return the last 5cids (and theChannelManagerwill at this point determine there are no pages left)
UI Component Overrides
The UI components used by ChannelList can be customized via WithComponents. Wrap ChannelList (or any ancestor) with WithComponents and pass the components you want to override.
import { ChannelList, WithComponents } from "stream-chat-react-native";
<WithComponents
overrides={{
ChannelPreview: CustomPreview,
EmptyStateIndicator: CustomEmpty,
}}
>
<ChannelList filters={filters} sort={sort} />
</WithComponents>;The following components can be overridden:
| Component | Description | Type | Default |
|---|---|---|---|
EmptyStateIndicator | Rendered when the channel list is empty and not loading via the ListEmptyComponent prop on the FlatList. | ComponentType | EmptyStateIndicator |
ChannelListFooterLoadingIndicator | Rendered when loadingNextPage from ChannelsContext is true via the ListFooterComponent prop on the FlatList. | ComponentType | ChannelListFooterLoadingIndicator |
ChannelListHeaderErrorIndicator | Rendered when error from ChannelsContext is true. | ComponentType | ChannelListHeaderErrorIndicator |
ChannelListHeaderNetworkDownIndicator | Rendered when isOnline from ChatContext is false. | ComponentType | ChannelListHeaderNetworkDownIndicator |
ListHeaderComponent | Rendered when provided if the channel list is not empty via the ListHeaderComponent prop on the FlatList. | ComponentType | undefined | - |
LoadingErrorIndicator | Rendered when error from ChannelsContext is true, and the channel list is empty and not loading. | ComponentType | LoadingErrorIndicator |
LoadingIndicator | Rendered when the channel list is empty and loading via the ListEmptyComponent prop on the FlatList. | ComponentType | ChannelListLoadingIndicator |
ChannelPreview | List item rendered by the underlying FlatList. | ComponentType | ChannelPreviewView |
ChannelPreviewAvatar | Avatar component rendered within ChannelPreview. | ComponentType | ChannelAvatar |
ChannelPreviewMessage | Message component rendered within ChannelPreview. | ComponentType | ChannelPreviewMessage |
ChannelPreviewMutedStatus | Channel muted status component rendered within ChannelPreview. | ComponentType | ChannelPreviewMutedStatus |
ChannelPreviewStatus | Status component rendered within ChannelPreview. | ComponentType | ChannelPreviewStatus |
ChannelPreviewTitle | Title component rendered within ChannelPreview. | ComponentType | ChannelPreviewTitle |
ChannelPreviewUnreadCount | Unread count component rendered within ChannelPreview. | ComponentType | ChannelPreviewUnreadCount |
ChannelPreviewTypingIndicator | Typing indicator component rendered within ChannelPreview. | ComponentType | ChannelPreviewTypingIndicator |
ChannelPreviewMessageDeliveryStatus | Message delivery status component rendered within ChannelPreview. | ComponentType | ChannelPreviewMessageDeliveryStatus |
ChannelDetailsBottomSheet | Channel details bottom sheet component that triggers when a user swipes a channel and selects the options menu. Make sure you have swipeActionsEnabled set to true to use this component. | ComponentType | ChannelDetailsBottomSheet |
Skeleton | Row item rendered in the LoadingIndicator. | ComponentType | Skeleton |
Channel List Event Listener Options
| Prop | Description | Type |
|---|---|---|
filters | Filter object passed internally to the client query function as a parameter. You can filter a query on built-in and custom fields on a Channel. | object |
sort | Sort object passed internally to the client query function as a parameter. You can sort a query on built-in and custom fields on a Channel. | object |