# Custom ChannelList

Let's look at how to make some customizations to [`ChannelList`](/chat/docs/sdk/react-native/v6/core-components/channel-list/).

### Customizing Event Handlers

`ChannelList` uses [Event Listeners](/chat/docs/javascript/event_object/) to dynamically update when changes occur.
If a new message is received, a user is added to a channel, or other events take place, the `ChannelList` will update its UI accordingly.
It's important to note that [`filters`](/chat/docs/sdk/react-native/v6/core-components/channel-list#filters/) don't apply to updates to the list from events.

The event type the `ChannelList` reacts to and its corresponding behavior can be overridden using the appropriate prop.

| [Event Type](/chat/docs/javascript/event_object/) | Default Behavior                                                   | Prop to override                                                                                                                                                                             |
| ------------------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channel.deleted`                                 | Remove channel from the list                                       | [onChannelDeleted](/chat/docs/sdk/react-native/v6/core-components/channel-list#onchanneldeleted/)                                                                                            |
| `channel.hidden`                                  | Remove channel from the list                                       | [onChannelHidden](/chat/docs/sdk/react-native/v6/core-components/channel-list#onchannelhidden/)                                                                                              |
| `channel.truncated`                               | Updates the channel                                                | [onChannelTruncated](/chat/docs/sdk/react-native/v6/core-components/channel-list#onchanneltruncated/)                                                                                        |
| `channel.updated`                                 | Updates the channel                                                | [onChannelUpdated](/chat/docs/sdk/react-native/v6/core-components/channel-list#onchannelupdated/)                                                                                            |
| `channel.visible`                                 | Adds the channel to the list                                       | [onChannelVisible](/chat/docs/sdk/react-native/v6/core-components/channel-list#onchannelvisible/)                                                                                            |
| `message.new`                                     | Moves the channel to top of the list                               | [lockChannelOrder](/chat/docs/sdk/react-native/v6/core-components/channel-list#lockchannelorder/), [onNewMessage](/chat/docs/sdk/react-native/v6/core-components/channel-list#onnewmessage/) |
| `notification.added_to_channel`                   | Adds the new channel to the top of the list and starts watching it | [onAddedToChannel](/chat/docs/sdk/react-native/v6/core-components/channel-list#onaddedtochannel/)                                                                                            |
| `notification.message_new`                        | Adds the new channel to the top of the list and starts watching it | [onNewMessageNotification](/chat/docs/sdk/react-native/v6/core-components/channel-list#onnewmessagenotification/)                                                                            |
| `notification.removed_from_channel`               | Removes the channel from the list                                  | [onRemovedFromChannel](/chat/docs/sdk/react-native/v6/core-components/channel-list#onremovedfromchannel/)                                                                                    |

Let's take an example of ChannelList component for [frozen channels](/chat/docs/javascript/disabling_channels/).

```tsx
const filters = {
  members: { $in: ['vishal'] },
  frozen: true
}

<ChannelList filters={filters} />
```

The `notification.message_new` event occurs when a message is received on a channel that is not loaded but the current user is a member of.
The default behavior when this event occurs is to query the channel the message is received on, then add the channel to the top of the list, irrespective of [`filters`](/chat/docs/sdk/react-native/v6/core-components/channel-list#filters/).
Thus, if new message appears in some unfrozen channel which current user is member of, it will be added to the list.
This may not be a desired behavior since the list is only supposed to show frozen channels.

In this case you can replace the default functionality by providing a custom [`onNewMessageNotification`](/chat/docs/sdk/react-native/v6/core-components/channel-list#onnewmessagenotification/) function as prop to `ChannelList` component.
`onNewMessageNotification` receives two parameters when called, `setChannels`, a setter function for the internal `channels` state, and `event`, the `Event` object received for the `notification.message_new` event.
These parameters can be used to create a function that achieves the desired custom behavior.

```tsx {6-19,23}
const filters = {
  members: { $in: ["vishal"] },
  frozen: true,
};

const customOnNewMessageNotification = async (setChannels, event) => {
  const eventChannel = event.channel;

  // If the channel is frozen, then don't add it to the list.
  if (!eventChannel?.id || !eventChannel.frozen) return;

  try {
    const newChannel = client.channel(eventChannel.type, eventChannel.id);
    await newChannel.watch();
    setChannels((channels) => [newChannel, ...channels]);
  } catch (error) {
    console.log(error);
  }
};

<ChannelList
  filters={filters}
  onNewMessageNotification={customOnNewMessageNotification}
/>;
```

Similarly, events other than notification.message_new can be handled as per application requirement.

### Replacing infinite scroll pagination with `Load More` button

`ChannelList` component accepts the [`List`](/chat/docs/sdk/react-native/v6/core-components/channel-list#list/) prop, which is a component to render the list of channels.
Default value for this prop is [`ChannelListMessenger`](/chat/docs/sdk/react-native/v6/ui-components/channel-preview-messenger/) and it consumes [`ChannelsContext`](/chat/docs/sdk/react-native/v6/contexts/channels-context/), which provides all the necessary values to implement infinite scroll pagination.
This component internally uses FlatList from react-native. And [`loadNextPage`](/chat/docs/sdk/react-native/v6/contexts/channels-context#loadnextpage/) function
from `ChannelsContext` is attached to [onEndReached](https://reactnative.dev/docs/flatlist#onendreached) prop of underlying FlatList to allow infinite scroll pagination.

Its possible to override the props on underlying FlatList by providing [`additionalFlatListProps`](/chat/docs/sdk/react-native/v6/core-components/channel-list#additionalflatlistprops/) prop to `ChannelList` or `ChannelListMessenger`.
Thus "Load More" button can be added as [ListFooterComponent](https://reactnative.dev/docs/flatlist#listfootercomponent) for underlying FlatList and infinite scroll pagination can be disabled by overriding
the [onEndReached](https://reactnative.dev/docs/flatlist#onendreached) prop for FlatList.

"Load More" button should invoke [`loadNextPage`](/chat/docs/sdk/react-native/v6/contexts/channels-context#loadnextpage/) function, when pressed.
Additionally, you can use pagination related flags such as `loadNextPage`, `hasNextPage`, `loadingChannels` to handle the visibility of this button.

![ChannelList](@chat-sdk/react-native/v6/_assets/guides/custom-channel-list/load_more_button.png)

```tsx
import { Button } from "react-native";
import { useChannelsContext } from "stream-chat-react-native";

const FooterLoadMoreButton = () => {
  const { loadingChannels, loadNextPage, hasNextPage } = useChannelsContext();

  if (loadingChannels || !hasNextPage) return null;

  return <Button title={"Load More"} onPress={loadNextPage} />;
};

<ChannelList
  additionalFlatListProps={{
    ListFooterComponent: FooterLoadMoreButton,
    onEndReached: () => null,
  }}
/>;
```

## Multiple Channel Lists

This example will focus on the specific use case where there are two `ChannelList` components in the same application.

The event listeners of both lists, let's say A and B, will pick up all `new.message` events for every channel, regardless of which list the `message` is sent in. If a message is sent in list B, the event listener in list A will also pick up the new `message` and bump the impacted `channel` to the top of the list. This is not the desired result for multiple lists, but there is a correct way to handle the routing of these messages.

### Using `channelRenderFilterFn` prop

The reason that a channel will automatically be bumped to the top of a list even though it's not part of the list is due to the default behavior. The ChannelList components will retrieve a channel from `client.activeChannels` if the channel doesn't already exist.

By using the `channelRenderFilterFn` prop we can apply custom filtering logic to the list of channels that are rendered. Since we have access to the entire channel object, we can filter on type, custom fields, or others.

```tsx
const customChannelFilterFunction = (channels: Channel[]) => {
  return channels.filter(/** your custom filter logic */);
};

<ChannelList
  channelRenderFilterFn={customChannelFilterFunction}
  filters={filters}
/>;
```


---

This page was last updated at 2026-04-22T14:09:38.114Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react-native/v6/guides/customize-channel-list/](https://getstream.io/chat/docs/sdk/react-native/v6/guides/customize-channel-list/).