Channel Detail

ChannelDetail is a composable surface for inspecting and managing a single channel. It renders channel info, members, pinned messages, shared media, and shared files, and adapts between a tabbed/docked layout (wide containers) and an inline hamburger-driven drawer (narrow containers).

It ships as a separate plugin entry point, stream-chat-react/channel-detail, with its own stylesheet. The component composes the generic SectionNavigator primitive with five default sections and provides the active channel to its children through ChannelDetailContext.

Installation

ChannelDetail is not exported from the package root. Import it (and the views, hooks, and primitives it ships with) from the plugin entry point, and load the plugin stylesheet alongside the core CSS:

import { ChannelDetail } from "stream-chat-react/channel-detail";

import "stream-chat-react/dist/css/index.css";
import "stream-chat-react/dist/css/channel-detail.css";

The examples below fold the recommended practices into working code — read the inline comments for the rationale behind each one.

Render it in a sized side panel

ChannelDetail measures its container with a ResizeObserver and switches layout accordingly, so it needs a sized parent to lay out correctly. It also never reads the active channel from context — you pass it in, so you stay in control of which channel it inspects.

import { useChannelStateContext } from "stream-chat-react";
import { ChannelDetail } from "stream-chat-react/channel-detail";

const ChannelInfoPanel = () => {
  // Pass `channel` explicitly — ChannelDetail does not pull it from context,
  // so you decide which channel the surface inspects.
  const { channel } = useChannelStateContext();

  return (
    // Give it a measurable, sized container. The ResizeObserver picks the
    // layout from this width: wide → docked `tabs`, narrow → `inline` drawer.
    <aside style={{ width: 360, height: "100%" }}>
      <ChannelDetail channel={channel} />
    </aside>
  );
};

With the five default sections, this renders Channel info, Members, Pinned messages, Photos & videos, and Files.

Open details from the channel header avatar

For the common "click the header avatar to open details in a modal" behavior, use AvatarWithChannelDetail rather than wiring a modal around ChannelDetail yourself.

import { ChannelHeader } from "stream-chat-react";
import { AvatarWithChannelDetail } from "stream-chat-react/channel-detail";

// No sizing or channel wiring needed here: the modal sizes the surface, and
// AvatarWithChannelDetail reads the active channel from ChannelStateContext.
<ChannelHeader Avatar={AvatarWithChannelDetail} />;

Adjust the surface instead of rebuilding it

Customize through the sections prop or by overriding individual views — everything you don't replace keeps its built-in behavior, including the confirmation dialogs that gate destructive actions (leave/delete, block/unblock).

import {
  ChannelDetail,
  defaultChannelDetailSections,
} from "stream-chat-react/channel-detail";

// Start from the defaults and tweak — here, drop the Files section — instead
// of assembling the surface from scratch. The sections you keep retain their
// behavior, so destructive actions stay gated behind their confirmation dialogs.
const sections = defaultChannelDetailSections.filter(
  (section) => section.id !== "channel-files",
);

<ChannelDetail channel={channel} sections={sections} />;

Layout

ChannelDetail switches between two layouts based on its container width:

  • tabs — a docked navigation sidebar with the active section beside it (the default).
  • inline — a single-column view with a hamburger button that opens the navigation as a drawer overlay.

The breakpoint is controlled by tabsLayoutMinWidth (default 640px). Below it, the component uses the inline layout. Set defaultLayout to choose the layout used before the container has been measured.

<ChannelDetail
  channel={channel}
  defaultLayout="inline"
  tabsLayoutMinWidth={720}
/>

UI Customization

Choose or reorder sections

Each section is a { id, NavButton, SectionContent } descriptor. Pass a sections array to add, remove, or reorder them. The plugin exports the default sections and the individual section descriptors so you can reuse them:

import {
  ChannelDetail,
  ChannelManagementView,
  ChannelMembersView,
  PinnedMessagesView,
  ChannelManagementNavButton,
  ChannelMembersNavButton,
  PinnedMessagesNavButton,
} from "stream-chat-react/channel-detail";

const sections = [
  {
    id: "channel-info",
    NavButton: ChannelManagementNavButton,
    SectionContent: ChannelManagementView,
  },
  {
    id: "channel-members",
    NavButton: ChannelMembersNavButton,
    SectionContent: ChannelMembersView,
  },
  {
    id: "pinned-messages",
    NavButton: PinnedMessagesNavButton,
    SectionContent: PinnedMessagesView,
  },
];

const App = ({ channel }) => (
  <ChannelDetail channel={channel} sections={sections} />
);

To replace the contents of a section while keeping its nav button, supply a custom SectionContent. A section's content component receives the current layout and can read the channel through useChannelDetailContext. See Views for the props each built-in view accepts.

Add a custom section

import type { SectionNavigatorSection } from "stream-chat-react/channel-detail";

const NotificationsSection: SectionNavigatorSection = {
  id: "notifications",
  NavButton: ({ select, selected }) => (
    <button onClick={select} aria-pressed={selected}>
      Notifications
    </button>
  ),
  SectionContent: () => <NotificationSettings />,
};

<ChannelDetail
  channel={channel}
  sections={[...defaultChannelDetailSections, NotificationsSection]}
/>;

Reading the channel from descendants

ChannelDetail wraps its content in ChannelDetailProvider. Custom views and nav buttons can read the active channel with useChannelDetailContext:

import { useChannelDetailContext } from "stream-chat-react/channel-detail";

const MemberCount = () => {
  const { channel } = useChannelDetailContext();
  return <span>{channel.data?.member_count} members</span>;
};

Calling useChannelDetailContext outside of ChannelDetail (or a ChannelDetailProvider) throws.

Props

ChannelDetail accepts every SectionNavigator prop except sections (whose shape is the same but whose default differs), plus the following.

NameDescriptionTypeDefault
channelThe channel to inspect and manage. Required.Channel-
sectionsThe sections to render, each a { id, NavButton, SectionContent } descriptor.SectionNavigatorSection[]defaultChannelDetailSections
defaultLayoutLayout used before the container is measured."tabs" | "inline""tabs"
tabsLayoutMinWidthMinimum container width (px) for the tabs layout; below it the inline layout is used.number640
classNameAdditional class name applied to the root element.string-

Exports

The plugin's main exports for this surface:

ExportDescription
ChannelDetailThe main component.
defaultChannelDetailSectionsThe five default section descriptors.
ChannelManagementNavButton, ChannelMembersNavButton, PinnedMessagesNavButton, ChannelMediaNavButton, ChannelFilesNavButtonThe default nav buttons for each section.
ChannelDetailProviderProvider that exposes the active channel.
useChannelDetailContextHook returning { channel } from the nearest provider.

The view components, search hooks, and primitives are documented separately in Views, SectionNavigator, and AvatarWithChannelDetail.