# Channel Detail Views

> **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/).

The views are the panels of [`ChannelDetail`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-detail/channel-detail/)'s five default sections. Each one is a section's `SectionContent`: it gets the current `layout` and reads the channel with `useChannelDetailContext()`. [Channel details and members](https://getstream.io/chat/docs/sdk/react/v15/features/channels/channel-details-and-members/#state-what-each-section-calls) explains what each one reads and calls.

| View                    | Section id        | Shows                                                                               |
| ----------------------- | ----------------- | ----------------------------------------------------------------------------------- |
| `ChannelManagementView` | `channel-info`    | The channel's avatar, name and online status, an edit form, and the channel actions |
| `ChannelMembersView`    | `channel-members` | The members, adding members, and one member's details                               |
| `PinnedMessagesView`    | `pinned-messages` | The pinned messages, with a search field                                            |
| `ChannelMediaView`      | `channel-media`   | A grid of the images and videos shared in the channel                               |
| `ChannelFilesView`      | `channel-files`   | The files and audio shared in the channel, by month                                 |

All of them come from `stream-chat-react/channel-detail`. To pass props to a view, replace the section's `SectionContent` with a component that renders the view with them, as in [Choose or reorder sections](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-detail/channel-detail/#choose-or-reorder-sections).

## ChannelManagementView

The Channel info section. It shows the channel's avatar and name, whether it's pinned or muted, and its online status. **Edit**, shown with the `update-channel` capability, switches to a form that changes the name and the image. Below are the channel actions.

It reads the online status from the channel in context, so render `ChannelDetail` inside a `Channel` for the same channel; without one it throws.

| Prop                         | Type                                                  | Default                             | Description                                                                                                 |
| ---------------------------- | ----------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `layout`                     | `SectionNavigatorLayout`                              | from the navigator                  | The current layout.                                                                                         |
| `channelManagementActionSet` | `ChannelManagementActionItem[]`                       | `defaultChannelManagementActionSet` | The channel actions, in order. `useBaseChannelManagementActionSetFilter` removes the ones that don't apply. |
| `uploadImage`                | `(file: File) => Promise<string> \| string`           | `Channel.uploadImage()`             | Uploads a new channel image and returns its URL.                                                            |
| `ViewModeComponent`          | `React.ComponentType<ChannelManagementInfoBodyProps>` | `ChannelManagementInfoBody`         | The body outside edit mode. Gets the filtered `actions`.                                                    |
| `EditModeComponent`          | `React.ComponentType<ChannelManagementEditBodyProps>` | `ChannelManagementEditBody`         | The edit form. Gets `uploadImage`.                                                                          |

Saving calls `Channel.updatePartial()` with `custom.name` and `custom.image`, and publishes a "Changes saved" or "Failed to save changes" notification.

An action is `{ type, Component }`. The `type`s of the defaults are `muteChannel`, `muteUser`, `blockUser`, `leaveChannel` and `deleteChat`, and their components are in `DefaultChannelManagementActions`: `MuteChannel`, `MuteUser`, `BlockUser`, `LeaveChannel` and `DeleteChat`. Leaving, deleting and blocking ask for confirmation first.

```tsx
import {
  DefaultChannelManagementActions,
  type ChannelManagementActionItem,
} from "stream-chat-react/channel-detail";

// Only muting and leaving, in this order.
export const channelManagementActionSet: ChannelManagementActionItem[] = [
  {
    Component: DefaultChannelManagementActions.MuteChannel,
    type: "muteChannel",
  },
  {
    Component: DefaultChannelManagementActions.LeaveChannel,
    type: "leaveChannel",
  },
];
```

[Channel details actions](https://getstream.io/chat/docs/sdk/react/v15/ui/customizing/actions/#channel-details-actions) shows how to add your own actions and which capabilities the filter checks.

## ChannelMembersView

The Members section. It has three modes:

- `browse`, the default: the member list, with a search field and the header actions;
- `add`: a search for users to add, from `defaultChannelMembersModeViews`;
- `memberDetail`: one member's details, opened by clicking a member.

`browse` and `memberDetail` are built in. Every other mode, `add` included, is looked up in the `modeViews` registry.

| Prop                       | Type                                                               | Default                                | Description                                                                     |
| -------------------------- | ------------------------------------------------------------------ | -------------------------------------- | ------------------------------------------------------------------------------- |
| `layout`                   | `SectionNavigatorLayout`                                           | from the navigator                     | The current layout. Passed on to `ChannelMemberDetail`.                         |
| `headerActionSet`          | `ChannelMembersHeaderActionItem[]`                                 | `defaultChannelMembersHeaderActionSet` | The actions in the header of the member list.                                   |
| `HeaderActions`            | `React.ComponentType<ChannelMembersHeaderActionsProps>`            | `DefaultHeaderActions`                 | Renders the header actions: `quick` ones as buttons, `menu` ones in a menu.     |
| `HeaderActionsMenuTrigger` | `React.ComponentType<ChannelMembersHeaderActionsMenuTriggerProps>` | `DefaultHeaderActionsMenuTrigger`      | The button that opens the menu of header actions.                               |
| `modeViews`                | `ChannelMembersViewModes`                                          | `defaultChannelMembersModeViews`       | Your modes, merged over the default ones. A mode with the same key replaces it. |

A header action is `{ type, component, placement, filter? }`. Its `component` gets the `modeController` and, in the menu, `closeMenu`. `placement` is `"quick"` or `"menu"`. The default set has one action, `addMembers`, a `quick` button that opens the `add` mode and needs `update-channel-members`. An action of your own can carry `filter({ channel })` to decide whether it shows.

A mode is `{ Title, Body }`, keyed by the name passed to `modeController.setMode()`. `Title` is a component, so it can read context. `Body` gets the `modeController`, `{ mode, setMode }`. A mode other than `browse` shows a back button that returns to the list:

```tsx
import {
  ChannelMembersView,
  useChannelDetailContext,
} from "stream-chat-react/channel-detail";
import type {
  ChannelMembersModeViewProps,
  ChannelMembersViewModes,
  SectionNavigatorSectionContentProps,
} from "stream-chat-react/channel-detail";

const InviteLinkBody = ({ modeController }: ChannelMembersModeViewProps) => {
  const { channel } = useChannelDetailContext();

  return (
    <div>
      <code>{`${window.location.origin}/join/${channel.id}`}</code>
      <button onClick={() => modeController.setMode("browse")} type="button">
        Done
      </button>
    </div>
  );
};

const modeViews: ChannelMembersViewModes = {
  invite: { Body: InviteLinkBody, Title: () => <>Invite link</> },
};

// Use it as the SectionContent of the "channel-members" section.
export const MembersSection = (props: SectionNavigatorSectionContentProps) => (
  <ChannelMembersView {...props} modeViews={modeViews} />
);
```

A header action calls `modeController.setMode("invite")` to open it.

The parts of the view are exported too:

- `ChannelMembersBrowseView`: the member list. Takes `onMemberSelect(member)`.
- `ChannelMembersAddView`: the `add` mode. Takes the `modeController` and `searchSource`, a `UserSearchSource` that replaces the default user search.
- `DefaultChannelMembersHeaderActions`: `AddMembers`, the button, and `AddMembersMenu`, the same action as a menu item.
- `useBaseChannelMembersHeaderActionSetFilter`: the filter `DefaultHeaderActions` applies.

### ChannelMemberDetail

One member's details: their avatar, name and presence, and the member actions.

| Prop                     | Type                        | Default                         | Description                                                                                            |
| ------------------------ | --------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `member`                 | `ChannelMemberResponse`     | required                        | The member to show.                                                                                    |
| `channelMemberActionSet` | `ChannelMemberActionItem[]` | `defaultChannelMemberActionSet` | The member actions, in order. `useBaseChannelMemberActionSetFilter` removes the ones that don't apply. |
| `onBack`                 | `() => void`                | none                            | Shows a back button that calls it.                                                                     |
| `layout`                 | `SectionNavigatorLayout`    | required                        | The current layout. The type requires it, but the view doesn't read it.                                |

An action is `{ type, Component }`. The defaults are `sendMessage`, `muteUser`, `blockUser` and `removeUser`, with their components in `DefaultChannelMemberActions`: `SendDirectMessage`, `MuteUser`, `BlockUser` and `RemoveUser`. An action's component reads the member with `useChannelMemberActionContext()`, which returns `{ member, memberDisplayName, targetUserId }`.

`ChannelMembersView` renders `ChannelMemberDetail` without a `channelMemberActionSet`, so the member list always opens the default actions. Your set applies only to a `ChannelMemberDetail` you render yourself.

## PinnedMessagesView, ChannelMediaView, ChannelFilesView

These three views list messages from a search limited to the channel. The pinned messages and files lists load the next page as the user scrolls; the media grid has Previous and Next buttons.

| View                 | Props                                            | Search hook               |
| -------------------- | ------------------------------------------------ | ------------------------- |
| `PinnedMessagesView` | `layout`, `searchSource?: MessageSearchSource`   | `usePinnedMessagesSearch` |
| `ChannelMediaView`   | `layout`, `itemsPerPage?: number`, 30 by default | `useChannelMediaSearch`   |
| `ChannelFilesView`   | `layout`                                         | `useChannelFilesSearch`   |

- **`PinnedMessagesView`** lists pinned messages with their author and date, and a search field that filters them by text. `searchSource` replaces its search; its filters and sort are used as they are. Selecting a message opens the channel with `openChannel` from `useWorkspaceNavigation()`, jumps its message list to the message and closes the modal. While the channel has no pinned messages, it shows `PinnedMessagesEmptyList`; when a search matches none, "No messages found".
- **`ChannelMediaView`** shows images and videos as a grid, with the video length, `itemsPerPage` at a time. Next loads more messages when the next page isn't loaded yet. Clicking an item opens it in the `Gallery` override, inside the `Modal` override.
- **`ChannelFilesView`** lists files and audio grouped by month, newest first. A file with a URL links to it.

To change one of them, replace it with a component of your own built on its search hook.

## Search & pagination hooks

Each list view loads through a hook that builds a search source for the channel. Use them to build your own list. They need `ChannelDetail` or a `ChannelDetailProvider` around them, and throw outside both.

| Hook                      | Returns                                                                                                              |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `useChannelMembersSearch` | `{ displayedMembers, handleSearchChange, hasMembers, membersSearchSource, resetMembersSearch, searchInputResetKey }` |
| `useChannelMediaSearch`   | `{ channelMediaSearchSource, hasNext, hasResultsLoaded, isLoading, mediaItems }`                                     |
| `useChannelFilesSearch`   | `{ channelFilesSearchSource, fileItems, groupCounts, hasResultsLoaded, isLoading, sections }`                        |
| `usePinnedMessagesSearch` | `{ displayedMessages, handleSearchChange, hasPinnedMessages, hasSearchResultsLoaded, pinnedMessagesSearchSource }`   |

- `useChannelMembersSearch` searches the channel's members with a `ChannelMemberSearchSource`. Until the first results arrive, `displayedMembers` holds the members the channel has loaded. `resetMembersSearch()` clears the query and reloads the list, and bumps `searchInputResetKey`, which resets a `ChannelDetailSearchInput`.
- `useChannelMediaSearch` and `useChannelFilesSearch` search the channel's messages with image and video attachments, or file and audio ones, and turn them into items with the helpers below.
- `usePinnedMessagesSearch` takes `{ searchSource }`. Without one, it searches the channel's messages with `pinned: true`, matching text with `$autocomplete`. It loads only while the channel has pinned messages.

```tsx
import {
  ChannelDetailListLoadingIndicator,
  useChannelMediaSearch,
} from "stream-chat-react/channel-detail";

export const MediaThumbnails = () => {
  const {
    channelMediaSearchSource,
    hasNext,
    hasResultsLoaded,
    isLoading,
    mediaItems,
  } = useChannelMediaSearch();

  if (isLoading && !hasResultsLoaded) return <span>Loading…</span>;

  return (
    <>
      <ul className="media-thumbnails">
        {mediaItems.map((item) => (
          <li key={item.id}>
            {item.type} from {item.user?.name ?? "unknown"}
          </li>
        ))}
      </ul>
      {hasNext && (
        // Without a new query, search() loads the next page.
        <button onClick={() => channelMediaSearchSource.search()} type="button">
          Load more
        </button>
      )}
      <ChannelDetailListLoadingIndicator
        searchSource={channelMediaSearchSource}
      />
    </>
  );
};
```

[Search sources](https://getstream.io/chat/docs/sdk/react/v15/state/loading-data/search-sources/) explains how a source loads its pages. The hooks' signatures are on [Loading data hooks](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/loading-data-hooks/#channel-details-search-hooks).

## Media & files data transforms

The media and files views turn messages into items with two helpers. Use them to build the same items from messages you have:

| Helper                  | Signature                                                                   | Returns                                                                                                                                                                                                                  |
| ----------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `toChannelMediaItems`   | `(messages: Array<MessageResponse \| LocalMessage>) => ChannelMediaItem[]`  | One item per image or video attachment that has something to show: `{ galleryItem, id, type, durationSeconds?, user? }`. `galleryItem` is what the `Gallery` takes, with the attachment's message as `message`.          |
| `toChannelFileSections` | `(messages: Array<MessageResponse \| LocalMessage>) => ChannelFileSections` | `{ groupCounts, items, sections }`: the file and audio attachments as `items`, grouped by month, newest first. `sections[i]` is a month, `{ key, timestamp? }`, and `groupCounts[i]` how many of the items belong to it. |

`toChannelFileSections` returns the shape `GroupedVirtuoso` takes. Each item is `{ attachment, id, createdAt? }`, with the attachment as it came. A section's `key` is `YYYY-MM`, or `unknown` for messages without a date.

```tsx
import type { LocalMessage } from "stream-chat";
import { toChannelFileSections } from "stream-chat-react/channel-detail";

export const FilesByMonth = ({ messages }: { messages: LocalMessage[] }) => {
  const { groupCounts, items, sections } = toChannelFileSections(messages);
  let start = 0;

  return (
    <>
      {sections.map((section, index) => {
        const sectionItems = items.slice(start, start + groupCounts[index]);
        start += groupCounts[index];

        return (
          <section key={section.key}>
            <h3>{section.key}</h3>
            <ul>
              {sectionItems.map((item) => (
                <li key={item.id}>{item.attachment.title}</li>
              ))}
            </ul>
          </section>
        );
      })}
    </>
  );
};
```

The attachment types each helper keeps are exported as `MEDIA_ATTACHMENT_TYPES`, `["image", "video"]`, and `FILE_ATTACHMENT_TYPES`, `["file", "audio"]`. The types are `ChannelMediaItem`, `ChannelMediaItemType`, `ChannelFileItem`, `ChannelFileSection`, `ChannelFileSections` and `ChannelFileAttachmentType`.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-detail/channel-detail-views/](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-detail/channel-detail-views/).