# Section Navigator

`SectionNavigator` is the generic, channel-agnostic navigation primitive that powers [`ChannelDetail`](/chat/docs/sdk/react/components/channel-detail/channel-detail/). 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

```tsx
import {
  SectionNavigator,
  SectionNavigatorHeader,
  useSectionNavigatorContext,
} from "stream-chat-react/channel-detail";

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

## Defining sections

A section is a `{ id, NavButton, SectionContent }` descriptor. `NavButton` renders the entry in the navigation rail/drawer; `SectionContent` renders the panel.

```tsx
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` (default `640`px) → `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).

```tsx
<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:

```tsx
// 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:

```tsx
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`:

```tsx
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).

```tsx
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`.


---

This page was last updated at 2026-06-25T10:44:12.706Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/components/channel-detail/section-navigator/](https://getstream.io/chat/docs/sdk/react/components/channel-detail/section-navigator/).