import {
SectionNavigator,
SectionNavigatorHeader,
useSectionNavigatorContext,
} from "stream-chat-react/channel-detail";
import "stream-chat-react/dist/css/channel-detail.css";Section Navigator
SectionNavigator is the generic, channel-agnostic navigation primitive that powers ChannelDetail. It renders a set of sections — each pairing a navigation button with a content panel — and switches between two layouts based on its container width:
tabs— a docked navigation rail rendered next to the active section's content.inline— a single-column view where navigation lives behind a hamburger button that opens a drawer overlay.
SectionNavigator knows nothing about channels; you can reuse it for any multi-section surface (settings panels, profile views, app menus). ChannelDetail is essentially SectionNavigator preconfigured with channel sections and a channel provider.
Installation
Defining sections
A section is a { id, NavButton, SectionContent } descriptor. NavButton renders the entry in the navigation rail/drawer; SectionContent renders the panel.
import { SectionNavigator } from "stream-chat-react/channel-detail";
const sections = [
{
// Give each section a stable, unique `id` — the navigator tracks the active
// route and history by `id`, so it must stay constant across renders.
id: "general",
NavButton: ({ select, selected }) => (
<button onClick={select} aria-pressed={selected}>
General
</button>
),
SectionContent: () => <GeneralSettings />,
},
{
id: "advanced",
NavButton: ({ select, selected }) => (
<button onClick={select} aria-pressed={selected}>
Advanced
</button>
),
SectionContent: () => <AdvancedSettings />,
},
];
const Settings = () => (
// Render inside a sized container — the default layout observer measures this
// parent to choose the layout (wide → `tabs`, narrow → `inline`).
<div style={{ width: "100%", height: "100%" }}>
<SectionNavigator sections={sections} />
</div>
);The first section is selected by default. Pass initialHistory to start on a different section.
Layout
The layout is resolved automatically by a ResizeObserver on the navigator's parent element:
- Container width
< tabsLayoutMinWidth(default640px) →inline. - Otherwise →
tabs.
Set defaultLayout for the value used before the first measurement, and onLayoutChange to react to layout changes (it fires once on mount and on every change).
<SectionNavigator
sections={sections}
defaultLayout="inline"
tabsLayoutMinWidth={720}
onLayoutChange={(layout) => console.log("layout:", layout)}
/>Controlled layout
Drive tabs/inline from width by default; pass layout only when you need to override the responsive behavior, which forces a layout and disables the observer:
// Controlled mode — opt out of width-based switching and pin the layout.
<SectionNavigator sections={sections} layout="tabs" />Custom layout observer
Override how the layout is computed with createLayoutObserver. The factory receives the root element, a setLayout callback, and tabsLayoutMinWidth, and returns a cleanup function:
const createLayoutObserver = ({ element, setLayout, tabsLayoutMinWidth }) => {
const onResize = () =>
setLayout(element.clientWidth < tabsLayoutMinWidth ? "inline" : "tabs");
window.addEventListener("resize", onResize);
onResize();
return () => window.removeEventListener("resize", onResize);
};
<SectionNavigator
sections={sections}
createLayoutObserver={createLayoutObserver}
/>;Navigation state with useSectionNavigatorContext
Section content (and nav buttons) can read and drive navigation through useSectionNavigatorContext:
import { useSectionNavigatorContext } from "stream-chat-react/channel-detail";
const NestedDetail = ({ goBack }) => {
// Read navigation state from inside section content rather than threading it
// down through props.
const { layout, historyPush, historyPop, isNavigationOpen, openNavigation } =
useSectionNavigatorContext();
// ...
};| Value | Description | Type |
|---|---|---|
layout | The current layout. | "tabs" | "inline" |
history | The current navigation stack (array of { id } routes). | SectionNavigatorRoute[] |
historyPush | Navigate to a route. In tabs layout this replaces history; in inline it pushes onto the stack. | (route: { id: string }) => void |
historyPop | Go back one entry (no-op at the root). | () => void |
isNavigationOpen | Whether the drawer overlay is open (inline layout only). | boolean |
openNavigation | Open the drawer overlay (inline layout). | () => void |
closeNavigation | Close the drawer overlay. | () => void |
SectionNavigatorHeader
Use SectionNavigatorHeader as the header for each section's content. It renders a Prompt.Header and, in inline layout where the navigation rail is hidden, prepends a hamburger button that opens the drawer. The hamburger is omitted when a goBack handler is set (nested views show a back button instead, so the two affordances don't compete).
import { SectionNavigatorHeader } from "stream-chat-react/channel-detail";
const GeneralSettings = () => (
<>
{/* Use SectionNavigatorHeader for each section's header: it shows the
hamburger in `inline` layout and a back button on nested views. */}
<SectionNavigatorHeader>General</SectionNavigatorHeader>
{/* body */}
</>
);SectionNavigatorHeader accepts all Prompt.Header props except LeadingContent, which it manages to render the hamburger.
Props
| Name | Description | Type | Default |
|---|---|---|---|
sections | The sections to render. Required. | SectionNavigatorSection[] | - |
defaultLayout | Layout used before the container is measured. | "tabs" | "inline" | "tabs" |
layout | Controlled layout. When set, the responsive observer is disabled. | "tabs" | "inline" | - |
tabsLayoutMinWidth | Minimum container width (px) for the tabs layout. | number | 640 |
onLayoutChange | Called whenever the resolved layout changes (and once on mount). | (layout) => void | - |
initialHistory | Initial navigation stack. Defaults to the first section. | SectionNavigatorRoute[] | - |
createLayoutObserver | Factory that wires up layout detection and returns a cleanup function. | SectionNavigatorLayoutObserverFactory | width observer |
className | Additional class name applied to the root element. Other div attributes are forwarded. | string | - |
SectionNavigatorSection
| Field | Description | Type |
|---|---|---|
id | Stable unique identifier for the section. | string |
NavButton | Navigation entry. Receives sectionId, selected, and select. | ComponentType<SectionNavigatorNavButtonProps> |
SectionContent | Panel rendered when the section is active. Receives layout. | ComponentType<SectionNavigatorSectionContentProps> |
SectionNavigatorNavButtonProps extends the native button props with sectionId: string, selected: boolean, and select: () => void.