# Channel lists

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

A channel list is the column of conversations a user picks from. This page covers what users can do in it, which state backs it, and what each row action calls. How the list renders and which parts you can replace is on [Channel list UI](https://getstream.io/chat/docs/sdk/react/v15/ui/components/channel-list-ui/). How lists load and stay current is on [Channel list paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/channel-list-paginators/).

## What users get

The user opens the app and sees their channels, the latest activity first. Each row shows the channel's avatar and name, the time and a preview of the last message, and a badge with the number of unread messages. A pin icon marks a channel they pinned, and a mute icon a channel they muted. Scrolling down loads more channels, and clicking a row opens the channel.

When the user points at a row, or moves focus into it, two buttons appear: one mutes the channel, and "more" opens a menu with Archive, Pin and Leave Channel. In a direct message channel the menu also has Block User. Each action confirms its result with a notification, such as "Channel archived".

The list keeps up as things change. When a message arrives, its channel moves to the top of a list sorted by the last message. When the user archives a channel, a list that leaves archived channels out drops it, and a list of archived channels picks it up.

## How it works across the layers

### State: the lists and their channels

A list is a `ChannelPaginator`: a query with filters and a sort order, and the channels it has loaded. The app registers it on `client.channelManager`. The manager keeps every registered list current as events arrive, and decides which list holds a channel that matches several. [Channel list paginators](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/channel-list-paginators/) explains both.

Each row is a `Channel` object, the same one the open channel and the rest of the app use. The row shows what the channel's stores hold, so a change made anywhere shows in the row too.

Three things about a channel belong to the current user alone: whether they archived it, pinned it or muted it.

- **Archived** and **pinned** are dates on the user's membership, `archived_at` and `pinned_at`. A list can filter on `archived` and `pinned` and sort on `pinned_at`, and the client applies the same filters and sort when they change.
- **Muted** is the channel's `muteStatus`. A muted channel's new messages don't count as unread.

[What channels and threads hold](https://getstream.io/chat/docs/sdk/react/v15/state/channels-and-threads/what-channels-and-threads-hold/#membership) has the fields and the events that change them.

### UI: the list and its rows

`ChannelNavigation` renders the list header, the search bar, and one `ChannelList` for each paginator on the channel manager. While a search is active, the search results take the lists' place; see [Search](https://getstream.io/chat/docs/sdk/react/v15/features/channels/search/). To place lists yourself, render `<ChannelList paginator={paginator} />` for each one.

`ChannelList` loads the first page when it mounts, unless its paginator has already loaded one, and the next page when the user scrolls near the end. `loadMoreThresholdPx` and `loadMoreDebounceMs` tune when the next page loads. It also starts the channel manager's event subscriptions, which every list shares. When a query fails, the list publishes an error notification.

Each row is a `ChannelListItem`. It reads its own channel's stores, so a row re-renders only when its channel changes. The unread badge is hidden while the channel is muted. [Channel list UI](https://getstream.io/chat/docs/sdk/react/v15/ui/components/channel-list-ui/#rows) lists what each part of a row reads.

The list holds no selection. Clicking a row calls `openChannel(channel)` from `useWorkspaceNavigation()`, and the layout decides where the channel opens. Without a layout, clicking a row does nothing; see [Navigation](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/navigation/).

### The row actions

The row actions are `ChannelListItemActionButtons`. Each one calls a method of the row's channel:

| Action                   | Where       | What it calls                                                        | Shown when                                                                          |
| ------------------------ | ----------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Mute, Unmute             | Next to row | `Channel.mute()`, `Channel.unmute()`                                 | The user has the `mute-channel` capability                                          |
| Archive, Unarchive       | Menu        | `Channel.archive()`, `Channel.unarchive()`                           | The user is a member                                                                |
| Block User, Unblock User | Menu        | `Channel.banUser(otherUserId, {})`, `Channel.unbanUser(otherUserId)` | The channel has two members, the user is one of them, and has `ban-channel-members` |
| Pin, Unpin               | Menu        | `Channel.pin()`, `Channel.unpin()`                                   | The user is a member                                                                |
| Leave Channel            | Menu        | `Channel.removeMembers({ members: [client.userId] })`                | The user has the `leave-channel` capability                                         |

Block User in this menu bans the other member from the channel. It isn't the same as `client.blockUser()`, which the message menu and channel details call. [Flagging, muting, blocking and bans](https://getstream.io/chat/docs/sdk/react/v15/features/moderation-and-safety/flagging-muting-blocking-and-bans/) explains the difference.

None of these actions changes the row before the server answers. The row and the lists change when the server's event arrives:

- **Mute.** `notification.channel_mutes_updated` updates the channel's `muteStatus`. The row shows the mute icon and hides the unread badge, and lists filtered on `muted` move the channel.
- **Archive and pin.** `member.updated` updates the user's membership. Lists filtered on `archived` or `pinned`, or sorted on `pinned_at`, move the channel.
- **Leave.** `notification.removed_from_channel` removes the channel from every list.

Each action reports its result through `client.notifications`: "Channel muted", "Channel archived", "Channel pinned", "Left channel" or "User blocked", or an error such as "Failed to leave channel". [What's optimistic and what isn't](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/whats-optimistic-and-what-isnt/) lists which state changes wait for the server.

### Hiding a channel

The rows have no Hide action, but the state layer has one. `Channel.hide()` hides the channel for the current user until a new message arrives in it, and `Channel.show()` shows it again. `Channel.hide({ clear_history: true })` also clears the channel's history for the user.

A hidden channel leaves every list whose filters don't mention `hidden`, when the server's `channel.hidden` event arrives. It comes back on `channel.visible`.

## Turning it on or off

Channel lists are always available. A list shows the channels that match its paginator's filters, so scope them to the user, for example with `members: { $in: [userId] }`. Without such a filter, the list can show channels the user isn't a member of.

Who sees which row action depends on the user's capabilities in the channel, which the server derives from their roles and the channel type's permissions. The table above lists them. Archive and pin need only membership, because they change the user's own settings. Change capabilities on the server; see [Permissions reference](https://getstream.io/chat/docs/javascript/permissions-reference/).

To show the rows without actions, set a component that renders nothing on the `ChannelListItemActionButtons` key:

```tsx
import type { PropsWithChildren } from "react";
import { WithComponents } from "stream-chat-react";

const NoRowActions = () => null;

// Declared outside the component, so WithComponents gets the same object on every render.
const overrides = { ChannelListItemActionButtons: NoRowActions };

export const WithoutRowActions = ({ children }: PropsWithChildren) => (
  <WithComponents overrides={overrides}>{children}</WithComponents>
);
```

## Customizing

### Which channels a list shows, and in what order

The paginator's filters and sort decide this. This sidebar shows an inbox with pinned channels on top, and the archived channels in a second list below it:

```tsx title="src/Sidebar.tsx"
import { useEffect } from "react";
import { ChannelPaginator } from "stream-chat";
import type { ChannelFilters, SortParamRequest } from "stream-chat";
import { ChannelNavigation, useChatContext } from "stream-chat-react";

const userId = "YOUR_USER_ID";

// Pinned channels first, the most recently pinned on top, then the latest message first.
const sort: SortParamRequest[] = [
  { direction: -1, field: "pinned_at" },
  { direction: -1, field: "last_message_at" },
];

const inboxFilters: ChannelFilters = {
  archived: false,
  members: { $in: [userId] },
};
const archiveFilters: ChannelFilters = {
  archived: true,
  members: { $in: [userId] },
};

export const Sidebar = () => {
  const { client } = useChatContext();

  useEffect(() => {
    const inbox = new ChannelPaginator({
      client,
      filters: inboxFilters,
      id: "inbox",
      sort,
    });
    const archive = new ChannelPaginator({
      client,
      filters: archiveFilters,
      id: "archive",
      sort,
    });
    client.channelManager.insertPaginator({ paginator: inbox });
    client.channelManager.insertPaginator({ paginator: archive });
    return () => {
      client.channelManager.removePaginator(inbox);
      client.channelManager.removePaginator(archive);
    };
  }, [client]);

  // Renders the header, the search bar, the inbox and then the archive.
  return <ChannelNavigation />;
};
```

When the user archives a channel from the inbox, `member.updated` arrives, and the channel moves to the archive list. When they pin one, it moves to the top of its list.

A list whose filters or sort change has to load again from the first page; see [Changing filters or sort](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/channel-list-paginators/#changing-filters-or-sort).

### What a row looks like

Replace the row, its look or its last-message line through `WithComponents`. [Channel list UI](https://getstream.io/chat/docs/sdk/react/v15/ui/components/channel-list-ui/#replacing-parts) lists the override keys and has an example, and the [Preview UI](https://getstream.io/chat/docs/sdk/react/v15/recipes/channel-list/preview-ui/) recipe builds a custom row.

### The row actions

The row actions have no prop: `ChannelListItemActionButtons` always renders the default actions, and the default set isn't exported. To change them, set your own component on the `ChannelListItemActionButtons` key. [Channel-list row actions](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/actions/#channel-list-row-actions) shows one, with its own capability check.

### Where a channel opens

The layout decides. [Moving between channels, threads and messages](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/moving-between-channels-threads-and-messages/#opening-a-channel-from-elsewhere) covers every way a channel opens, and the [⌘/ctrl-click opens beside](https://getstream.io/chat/docs/sdk/react/v15/recipes/layout/ctrl-click-opens-beside/) recipe opens a channel next to the current one.

## Reference

- [ChannelList](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-list/channellist/): the `ChannelList` props.
- [Channel Preview](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-list/channel-preview/): the row components and their props.
- [ChannelListContext](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-list/channellistcontext/): the context below `ChannelLists`.
- [ComponentContext](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel/componentcontext/): the override keys, including `ChannelListItemActionButtons`.
- [Channel manager and ChannelPaginator](https://getstream.io/chat/docs/sdk/react/v15/reference/state/channel-manager-and-channelpaginator/): the state APIs.
- [Channel and thread hooks](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/channel-and-thread-hooks/): hooks such as `useChannelMembershipState`.
- [Loading data hooks](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/loading-data-hooks/): hooks such as `useChannelPaginatorState`.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-lists/](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-lists/).