# Section Navigator

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

`SectionNavigator` shows a set of sections, each a navigation button paired with a panel, and moves between them. [`ChannelDetail`](https://getstream.io/chat/docs/sdk/react/v15/component-reference/channel-detail/channel-detail/) is a `SectionNavigator` with five channel sections. It knows nothing about channels, so you can use it for any panel with sections, such as settings. [Moving between channels, threads and messages](https://getstream.io/chat/docs/sdk/react/v15/ui/layout/moving-between-channels-threads-and-messages/#inside-channel-details) describes how users move around channel details.

## Installation

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

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

The navigator's styles are in `channel-detail.css`.

## Defining sections

A section is a `{ id, NavButton, SectionContent }` object. `NavButton` renders the section's entry in the list of sections, and `SectionContent` its panel:

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

const navButton =
  (label: string) =>
  ({ select, selected }: SectionNavigatorNavButtonProps) => (
    <button aria-pressed={selected} onClick={select} type="button">
      {label}
    </button>
  );

const GeneralSettings = () => (
  <>
    <SectionNavigatorHeader title="General" />
    <p>Language and time zone</p>
  </>
);

const AdvancedSettings = () => (
  <>
    <SectionNavigatorHeader title="Advanced" />
    <p>Developer options</p>
  </>
);

// Declared once: ids identify the sections in the navigation history.
const sections: SectionNavigatorSection[] = [
  {
    id: "general",
    NavButton: navButton("General"),
    SectionContent: GeneralSettings,
  },
  {
    id: "advanced",
    NavButton: navButton("Advanced"),
    SectionContent: AdvancedSettings,
  },
];

export const Settings = () => (
  <div style={{ height: "100%", width: "100%" }}>
    <SectionNavigator sections={sections} />
  </div>
);
```

The first section opens first. Pass `initialHistory`, such as `[{ id: "advanced" }]`, to start on another one.

When `sections` changes and the open section is no longer in it, the navigator opens the first section.

## Layout

The navigator has two layouts, the values of `SECTION_NAVIGATOR_LAYOUT`:

- `tabs`: the list of sections beside the open one.
- `inline`: one section at a time. `SectionNavigatorHeader` shows a menu button that opens the list as a drawer. Escape, a click outside the drawer or picking a section closes it.

By default, a `ResizeObserver` measures the navigator's parent element. While it's narrower than `tabsLayoutMinWidth`, 640px by default, the layout is `inline`; otherwise it's `tabs`. A width of 0, such as a hidden parent, changes nothing. Before the first measurement, and where `ResizeObserver` doesn't exist, the layout is `defaultLayout`.

The root element carries the layout as `data-layout`, and the class `str-chat__section-navigator--inline` in the inline layout. `onLayoutChange` is called with the layout once on mount and on every change.

```tsx
const logLayout = (layout: SectionNavigatorLayout) =>
  console.log("layout:", layout);

export const MeasuredSettings = () => (
  <SectionNavigator
    defaultLayout="inline"
    onLayoutChange={logLayout}
    sections={sections}
    tabsLayoutMinWidth={720}
  />
);
```

### Controlled layout

Pass `layout` to fix the layout. While it's set, nothing is measured:

```tsx
<SectionNavigator layout="tabs" sections={sections} />
```

### Custom layout observer

`createLayoutObserver` replaces the measuring. It receives the navigator's root `element`, `setLayout` and `tabsLayoutMinWidth`, and returns a cleanup function. This one follows the window's width:

```tsx
const observeWindowWidth: SectionNavigatorLayoutObserverFactory = ({
  setLayout,
  tabsLayoutMinWidth,
}) => {
  const update = () =>
    setLayout(window.innerWidth < tabsLayoutMinWidth ? "inline" : "tabs");
  update();
  window.addEventListener("resize", update);
  return () => window.removeEventListener("resize", update);
};

export const WindowSizedSettings = () => (
  <SectionNavigator
    createLayoutObserver={observeWindowWidth}
    sections={sections}
  />
);
```

Declare the factory once. A new function on every render measures again on every render.

## Navigation state with `useSectionNavigatorContext`

Sections and nav buttons read and change the navigation with `useSectionNavigatorContext()`. Here the General section links to the Advanced one, and Advanced shows a back button when it was opened from another section:

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

export const GeneralSettings = () => {
  const { historyPush } = useSectionNavigatorContext();

  return (
    <>
      <SectionNavigatorHeader title="General" />
      <button onClick={() => historyPush({ id: "advanced" })} type="button">
        Advanced settings
      </button>
    </>
  );
};

export const AdvancedSettings = () => {
  const { history, historyPop } = useSectionNavigatorContext();
  // Only the inline layout keeps earlier sections in the history.
  const canGoBack = history.length > 1;

  return (
    <>
      <SectionNavigatorHeader
        goBack={canGoBack ? historyPop : undefined}
        title="Advanced"
      />
      <p>Developer options</p>
    </>
  );
};
```

Routes are section ids. A route that matches no section opens the first section, so a view inside a section, such as one member's details, keeps its own state instead: `ChannelMembersView` and `ChannelManagementView` do.

| Value              | Type                                     | Description                                                                                                         |
| ------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `layout`           | `SectionNavigatorLayout`                 | The current layout.                                                                                                 |
| `history`          | `SectionNavigatorRoute[]`                | The navigation stack, `{ id }` routes. The last one is open.                                                        |
| `historyPush`      | `(route: SectionNavigatorRoute) => void` | Opens a route. In `tabs`, it replaces the history; in `inline`, it adds to it. Pushing the open route does nothing. |
| `historyPop`       | `() => void`                             | Goes back one route. Does nothing at the first one.                                                                 |
| `isNavigationOpen` | `boolean`                                | Whether the drawer is open, in the `inline` layout.                                                                 |
| `openNavigation`   | `() => void`                             | Opens the drawer.                                                                                                   |
| `closeNavigation`  | `() => void`                             | Closes the drawer.                                                                                                  |

Outside a `SectionNavigator`, the hook doesn't throw. It returns the `tabs` layout, an empty history and functions that do nothing.

## `SectionNavigatorHeader`

`SectionNavigatorHeader` is the header for a section's panel. In the `inline` layout it shows a menu button that opens the drawer, except when `goBack` is set: a view with a back button shows no menu button.

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

export const NotificationsHeader = () => {
  const { close } = useModalContext();

  return (
    <SectionNavigatorHeader
      close={close}
      description="Choose what you hear about"
      title="Notifications"
    />
  );
};
```

| Prop                       | Type                  | Default                    | Description                                                                                  |
| -------------------------- | --------------------- | -------------------------- | -------------------------------------------------------------------------------------------- |
| `title`                    | `React.ReactNode`     | required                   | The header's title.                                                                          |
| `description`              | `string`              | none                       | A line under the title.                                                                      |
| `goBack`                   | `() => void`          | none                       | Shows a back button that calls it, and hides the menu button.                                |
| `close`                    | `() => void`          | none                       | Shows a close button that calls it. The channel details views pass the modal's `close`.      |
| `TrailingContent`          | `React.ComponentType` | none                       | Rendered at the end, before the close button.                                                |
| `className`                | `string`              | none                       | Added to the header's root element.                                                          |
| `titleId`, `descriptionId` | `string`              | ids derived from the modal | The ids of the title and description elements, for `aria-labelledby` and `aria-describedby`. |

The menu button's label is the translation key `channelDetail.sectionNavigatorHeader.openMenu.ariaLabel`, "Open menu".

## Props

| Prop                   | Type                                       | Default                                  | Description                                               |
| ---------------------- | ------------------------------------------ | ---------------------------------------- | --------------------------------------------------------- |
| `sections`             | `SectionNavigatorSection[]`                | required                                 | The sections, in order.                                   |
| `defaultLayout`        | `SectionNavigatorLayout`                   | `"tabs"`                                 | The layout before the first measurement.                  |
| `layout`               | `SectionNavigatorLayout`                   | none                                     | A fixed layout. While it's set, nothing is measured.      |
| `tabsLayoutMinWidth`   | `number`                                   | `640`                                    | The narrowest width, in pixels, for the `tabs` layout.    |
| `onLayoutChange`       | `(layout: SectionNavigatorLayout) => void` | none                                     | Called with the layout on mount and whenever it changes.  |
| `initialHistory`       | `SectionNavigatorRoute[]`                  | the first section                        | The navigation stack at the start.                        |
| `createLayoutObserver` | `SectionNavigatorLayoutObserverFactory`    | a `ResizeObserver` on the parent element | Measures the layout. Returns a cleanup function.          |
| `className`            | `string`                                   | none                                     | Added to the root element, `str-chat__section-navigator`. |

Other `div` attributes go to the root element.

### `SectionNavigatorSection`

| Field            | Type                                                 | Description                                                            |
| ---------------- | ---------------------------------------------------- | ---------------------------------------------------------------------- |
| `id`             | `string`                                             | The section's id. Keep it unique and stable: the history refers to it. |
| `NavButton`      | `ComponentType<SectionNavigatorNavButtonProps>`      | The entry in the list of sections.                                     |
| `SectionContent` | `ComponentType<SectionNavigatorSectionContentProps>` | The panel, shown while the section is open. Gets `layout`.             |

`SectionNavigatorNavButtonProps` are the `button` props plus `sectionId: string`, `selected: boolean` and `select: () => void`. `select` opens the section and closes the drawer.

The other types: `SectionNavigatorLayout` is a `string`, `"tabs"` or `"inline"`. `SectionNavigatorRoute` is `{ id: string }`. `SectionNavigatorLayoutObserverFactory` is `({ element, setLayout, tabsLayoutMinWidth }) => (() => void) | void`.

## Related hooks

| Hook                           | Returns                                                |
| ------------------------------ | ------------------------------------------------------ |
| `useSectionNavigatorContext()` | the layout, the history and the drawer state, as above |
| `useChannelDetailContext()`    | inside `ChannelDetail`, `{ channel }`                  |

---

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