# App shell and navigation

> Build native menus, toolbars, split views, and tab navigation.

Canonical: https://natui.dev/docs/components/app-shell-navigation

## MenuBar [#menubar]

Defines the application menu bar from serializable `MenuSpec` data. It must be a direct child of the React root.

### MenuBarProps

The application menu bar. Non-visual: must be a direct child of the root;
hosts hoist it to NSApp.mainMenu / a WinUI MenuBar row. Hosts never emit
select for disabled items, dividers, or submenu parents.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `menus` | `MenuSpec[]` | Yes |  |
| `onSelect` | `((id: string) => void) \| undefined` | No |  |
| `key` | `string \| number \| undefined` | No |  |

```tsx
<MenuBar
  menus={[
    {
      id: 'file',
      label: 'File',
      items: [{ id: 'new', label: 'New', shortcut: 'cmd+n' }],
    },
  ]}
  onSelect={(id) => handleCommand(id)}
/>
```

macOS prepends its standard application menu. Command-role items such as `copy`, `paste`, and `quit` invoke platform commands and do not emit `onSelect`. The `about` role opens the standard About panel on macOS and is inert on Windows.

## Toolbar [#toolbar]

Defines native window toolbar items from `ToolbarItemSpec[]`. It must be a direct root child.

### ToolbarProps

The window toolbar (NSToolbar unified style / WinUI CommandBar).
Non-visual: must be a direct child of the root. Search items are
uncontrolled on the wire: text fires onSearch and is never echoed back.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | `ToolbarItemSpec[]` | Yes |  |
| `onAction` | `((id: string) => void) \| undefined` | No |  |
| `onSearch` | `((value: string) => void) \| undefined` | No |  |
| `key` | `string \| number \| undefined` | No |  |

```tsx
<Toolbar
  items={[
    { type: 'button', id: 'add', label: 'Add', systemImage: 'plus' },
    { type: 'flexibleSpace' },
    { type: 'search', id: 'search', placeholder: 'Filter' },
  ]}
  onAction={handleAction}
  onSearch={setQuery}
/>
```

Toolbar search text is host-local and reported through `onSearch`. Windows always places search items in the left region and may rebuild the toolbar after prop changes, which drops active search text and focus.

## SplitView, Sidebar, and Detail [#splitview-sidebar-and-detail]

`SplitView` routes `Sidebar` and `Detail` children into native navigation slots. Use exactly one of each. macOS chooses the first matching child, while Windows assigns each matching child as it is attached, so duplicate slots have platform-dependent results.

### SplitViewProps

Two-pane navigation split. Children are routed by kind: the first Sidebar
child fills the sidebar column, the first Detail child the detail column
(order-independent; extras are ignored with a warning). `value` optionally
controls sidebar visibility.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | No |  |
| `value` | `"all" \| "detailOnly" \| undefined` | No | Controlled sidebar visibility. SplitView is always controlled on the wire: there is no host-local visibility state, so an app that omits `value` still receives change events for user collapses, but any later re-render restores the sidebar. Provide `value` + `onChange` to keep it. |
| `sidebarWidth` | `number \| undefined` | No |  |
| `minSidebarWidth` | `number \| undefined` | No |  |
| `maxSidebarWidth` | `number \| undefined` | No |  |
| `onChange` | `((value: "all" \| "detailOnly") => void) \| undefined` | No |  |
| `padding` | `number \| EdgeInsets \| undefined` | No |  |
| `background` | `string \| undefined` | No |  |
| `cornerRadius` | `number \| undefined` | No |  |
| `frame` | `Frame \| undefined` | No |  |
| `opacity` | `number \| undefined` | No |  |
| `disabled` | `boolean \| undefined` | No |  |
| `hidden` | `boolean \| undefined` | No |  |
| `color` | `string \| undefined` | No | Foreground color. |
| `help` | `string \| undefined` | No | Tooltip. |
| `tag` | `string \| undefined` | No | Stable row identity for selectable containers (List/Table selection). A selectable List reports and receives selection as its rows' tags. |
| `badge` | `string \| number \| undefined` | No | Badge shown on Tab items and List rows (counts, short strings). |
| `accessibilityLabel` | `string \| undefined` | No | Assistive-tech label (VoiceOver / Narrator). |
| `accessibilityHint` | `string \| undefined` | No | Assistive-tech hint describing the result of activating the element. |
| `accessibilityIdentifier` | `string \| undefined` | No | Stable identifier for UI automation (AX identifier / AutomationId). |
| `key` | `string \| number \| undefined` | No |  |

### SidebarProps


| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | No |  |
| `padding` | `number \| EdgeInsets \| undefined` | No |  |
| `background` | `string \| undefined` | No |  |
| `cornerRadius` | `number \| undefined` | No |  |
| `frame` | `Frame \| undefined` | No |  |
| `opacity` | `number \| undefined` | No |  |
| `disabled` | `boolean \| undefined` | No |  |
| `hidden` | `boolean \| undefined` | No |  |
| `color` | `string \| undefined` | No | Foreground color. |
| `help` | `string \| undefined` | No | Tooltip. |
| `tag` | `string \| undefined` | No | Stable row identity for selectable containers (List/Table selection). A selectable List reports and receives selection as its rows' tags. |
| `badge` | `string \| number \| undefined` | No | Badge shown on Tab items and List rows (counts, short strings). |
| `accessibilityLabel` | `string \| undefined` | No | Assistive-tech label (VoiceOver / Narrator). |
| `accessibilityHint` | `string \| undefined` | No | Assistive-tech hint describing the result of activating the element. |
| `accessibilityIdentifier` | `string \| undefined` | No | Stable identifier for UI automation (AX identifier / AutomationId). |
| `key` | `string \| number \| undefined` | No |  |

### DetailProps


| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | No |  |
| `padding` | `number \| EdgeInsets \| undefined` | No |  |
| `background` | `string \| undefined` | No |  |
| `cornerRadius` | `number \| undefined` | No |  |
| `frame` | `Frame \| undefined` | No |  |
| `opacity` | `number \| undefined` | No |  |
| `disabled` | `boolean \| undefined` | No |  |
| `hidden` | `boolean \| undefined` | No |  |
| `color` | `string \| undefined` | No | Foreground color. |
| `help` | `string \| undefined` | No | Tooltip. |
| `tag` | `string \| undefined` | No | Stable row identity for selectable containers (List/Table selection). A selectable List reports and receives selection as its rows' tags. |
| `badge` | `string \| number \| undefined` | No | Badge shown on Tab items and List rows (counts, short strings). |
| `accessibilityLabel` | `string \| undefined` | No | Assistive-tech label (VoiceOver / Narrator). |
| `accessibilityHint` | `string \| undefined` | No | Assistive-tech hint describing the result of activating the element. |
| `accessibilityIdentifier` | `string \| undefined` | No | Stable identifier for UI automation (AX identifier / AutomationId). |
| `key` | `string \| number \| undefined` | No |  |

```tsx
<SplitView value={visibility} onChange={setVisibility} sidebarWidth={220}>
  <Sidebar>{navigation}</Sidebar>
  <Detail>{content}</Detail>
</SplitView>
```

Use `value="all"` or `value="detailOnly"` to control visibility. macOS can report a native visibility change through `onChange`. Windows has no native visibility affordance, does not emit `onChange` for split visibility, and ignores minimum and maximum sidebar widths.

## TabView and Tab [#tabview-and-tab]

### TabViewProps

Controlled tab container; children are Tab elements. Tab clicks are optimistic.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | No |  |
| `value` | `string` | Yes | The selected Tab's `id`. |
| `onChange` | `((id: string) => void) \| undefined` | No |  |
| `padding` | `number \| EdgeInsets \| undefined` | No |  |
| `background` | `string \| undefined` | No |  |
| `cornerRadius` | `number \| undefined` | No |  |
| `frame` | `Frame \| undefined` | No |  |
| `opacity` | `number \| undefined` | No |  |
| `disabled` | `boolean \| undefined` | No |  |
| `hidden` | `boolean \| undefined` | No |  |
| `color` | `string \| undefined` | No | Foreground color. |
| `help` | `string \| undefined` | No | Tooltip. |
| `tag` | `string \| undefined` | No | Stable row identity for selectable containers (List/Table selection). A selectable List reports and receives selection as its rows' tags. |
| `badge` | `string \| number \| undefined` | No | Badge shown on Tab items and List rows (counts, short strings). |
| `accessibilityLabel` | `string \| undefined` | No | Assistive-tech label (VoiceOver / Narrator). |
| `accessibilityHint` | `string \| undefined` | No | Assistive-tech hint describing the result of activating the element. |
| `accessibilityIdentifier` | `string \| undefined` | No | Stable identifier for UI automation (AX identifier / AutomationId). |
| `key` | `string \| number \| undefined` | No |  |

### TabProps


| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes |  |
| `title` | `string` | Yes |  |
| `systemImage` | `string \| undefined` | No |  |
| `children` | `ReactNode` | No |  |
| `padding` | `number \| EdgeInsets \| undefined` | No |  |
| `background` | `string \| undefined` | No |  |
| `cornerRadius` | `number \| undefined` | No |  |
| `frame` | `Frame \| undefined` | No |  |
| `opacity` | `number \| undefined` | No |  |
| `disabled` | `boolean \| undefined` | No |  |
| `hidden` | `boolean \| undefined` | No |  |
| `color` | `string \| undefined` | No | Foreground color. |
| `help` | `string \| undefined` | No | Tooltip. |
| `tag` | `string \| undefined` | No | Stable row identity for selectable containers (List/Table selection). A selectable List reports and receives selection as its rows' tags. |
| `badge` | `string \| number \| undefined` | No | Badge shown on Tab items and List rows (counts, short strings). |
| `accessibilityLabel` | `string \| undefined` | No | Assistive-tech label (VoiceOver / Narrator). |
| `accessibilityHint` | `string \| undefined` | No | Assistive-tech hint describing the result of activating the element. |
| `accessibilityIdentifier` | `string \| undefined` | No | Stable identifier for UI automation (AX identifier / AutomationId). |
| `key` | `string \| number \| undefined` | No |  |

```tsx
<TabView value={tab} onChange={setTab}>
  <Tab id="overview" title="Overview" systemImage="info.circle">
    <Text>Summary</Text>
  </Tab>
  <Tab id="settings" title="Settings" badge={2}>
    <Text>Preferences</Text>
  </Tab>
</TabView>
```

`TabView` is controlled by a child `Tab` id. A tab click updates native selection optimistically, then reports the id through `onChange`.

The macOS host currently renders plain tab titles without the provided tab icon or badge. Windows renders native tab items.

## Verification note [#verification-note]

These seven app-shell kinds are exercised by the macOS kitchen-sink real-window suite. They are compile-checked on Windows but do not yet have equivalent Windows GUI end-to-end coverage.