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 filters and sort to avoid unnecessary re-queries.
  • Keep onSelect lightweight and handle navigation outside of render.
  • Handle notification.message_new/message.new if you need strict filtering on real-time updates.
  • Avoid passing Channel instances through navigation params; store them in state instead.
  • Use sensible loadMoreThreshold values to reduce extra queries.

General Usage

ChannelList renders channels in a FlatList.

Note: Define the component within OverlayProvider and Chat so the required contexts are available.

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>;

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 (via useChannelsContext) - provides channel list state and actions

Props

PropDescriptionType
filtersFilter 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
sortSort 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
optionsOptions 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
channelRenderFilterFnOptional 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[]
onSelectCalled 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
additionalFlatListPropsAdditional props provided to the underlying FlatList.object
loadMoreThresholdSets the onEndReachedThreshold. We recommend 0.1; higher values may trigger extra channelQuery calls. Defaults to 0.1.number
lockChannelOrderLocks 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
maxUnreadCountMax unread badge value. Cannot exceed 255 (backend limit). Defaults to 255.number
numberOfSkeletonsThe number of Skeleton items to display in the LoadingIndicator. Defaults to 6.number
onAddedToChannelOverride 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
onChannelDeletedOverride 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
onChannelHiddenOverride 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
onChannelMemberUpdatedOverride 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
onChannelVisibleOverride 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
onChannelTruncatedOverride 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
onChannelUpdatedOverride 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
onNewMessageNotificationOverride 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
onNewMessageOverride 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
onRemovedFromChannelOverride 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
setFlatlistRefCallback function to access the underlying FlatList ref.(ref) => void
mutedStatusPositionPosition of the muted status component within the ChannelPreview. Defaults to inlineTitle.trailingBottom | inlineTitle
swipeActionsEnabledIf true, the user can swipe to perform actions on a channel. Defaults to true.boolean
queryChannelsOverrideA 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:

  1. On the first page, 10 items will be loaded, which will be the first 10 cids
  2. On the second page, queryChannelsOverride will be invoked with offset: 10 and limit: 10, so cids 10 through 19 will be loaded
  3. On the last page, queryChannelsOverride will be invoked with offset: 10 and limit: 10, and so our implementation will return the last 5 cids (and the ChannelManager will 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:

ComponentDescriptionTypeDefault
EmptyStateIndicatorRendered when the channel list is empty and not loading via the ListEmptyComponent prop on the FlatList.ComponentTypeEmptyStateIndicator
ChannelListFooterLoadingIndicatorRendered when loadingNextPage from ChannelsContext is true via the ListFooterComponent prop on the FlatList.ComponentTypeChannelListFooterLoadingIndicator
ChannelListHeaderErrorIndicatorRendered when error from ChannelsContext is true.ComponentTypeChannelListHeaderErrorIndicator
ChannelListHeaderNetworkDownIndicatorRendered when isOnline from ChatContext is false.ComponentTypeChannelListHeaderNetworkDownIndicator
ListHeaderComponentRendered when provided if the channel list is not empty via the ListHeaderComponent prop on the FlatList.ComponentType | undefined-
LoadingErrorIndicatorRendered when error from ChannelsContext is true, and the channel list is empty and not loading.ComponentTypeLoadingErrorIndicator
LoadingIndicatorRendered when the channel list is empty and loading via the ListEmptyComponent prop on the FlatList.ComponentTypeChannelListLoadingIndicator
ChannelPreviewList item rendered by the underlying FlatList.ComponentTypeChannelPreviewView
ChannelPreviewAvatarAvatar component rendered within ChannelPreview.ComponentTypeChannelAvatar
ChannelPreviewMessageMessage component rendered within ChannelPreview.ComponentTypeChannelPreviewMessage
ChannelPreviewMutedStatusChannel muted status component rendered within ChannelPreview.ComponentTypeChannelPreviewMutedStatus
ChannelPreviewStatusStatus component rendered within ChannelPreview.ComponentTypeChannelPreviewStatus
ChannelPreviewTitleTitle component rendered within ChannelPreview.ComponentTypeChannelPreviewTitle
ChannelPreviewUnreadCountUnread count component rendered within ChannelPreview.ComponentTypeChannelPreviewUnreadCount
ChannelPreviewTypingIndicatorTyping indicator component rendered within ChannelPreview.ComponentTypeChannelPreviewTypingIndicator
ChannelPreviewMessageDeliveryStatusMessage delivery status component rendered within ChannelPreview.ComponentTypeChannelPreviewMessageDeliveryStatus
ChannelDetailsBottomSheetChannel 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.ComponentTypeChannelDetailsBottomSheet
SkeletonRow item rendered in the LoadingIndicator.ComponentTypeSkeleton

Channel List Event Listener Options

PropDescriptionType
filtersFilter 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
sortSort 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