---
title: Navigation Menu
description: Navigate a website through links and animated shared panels.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# Navigation Menu

> Navigate a website through links and animated shared panels.

Use NavigationMenu for website destinations, with direct links and expandable link panels. Use [DropdownMenu](https://moraine.subf.dev/components/dropdown-menu.md) for a menu of actions.

## Basic usage

```tsx
import { NavigationMenu } from 'moraine'

export function Example() {
  return (
    <NavigationMenu aria-label="Main navigation">
      <NavigationMenu.List>
        <NavigationMenu.Item value="products">
          <NavigationMenu.Trigger>Products</NavigationMenu.Trigger>
          <NavigationMenu.Content>
            <NavigationMenu.Link href="/overview" closeOnClick>
              Overview
            </NavigationMenu.Link>
          </NavigationMenu.Content>
        </NavigationMenu.Item>
        <NavigationMenu.Item>
          <NavigationMenu.Link href="/docs">Documentation</NavigationMenu.Link>
        </NavigationMenu.Item>
      </NavigationMenu.List>
    </NavigationMenu>
  )
}
```

## Anatomy

```text
NavigationMenu [component; slot=root]
├── NavigationMenu.List [part; slot=list]
│   └── NavigationMenu.Item [part; slot=item]
│       └── NavigationMenu.Trigger [part; slot=trigger]
│           └── triggerIcon [slot]
└── NavigationMenu.Content [part; slot=content]
    └── NavigationMenu.Link [part; slot=link]
```

## Usage

### Basic

Combine expandable panels with direct links. Use `NavigationMenu.Content` for each panel and `NavigationMenu.Link` for its destinations.

```tsx
import { NavigationMenu } from 'moraine'

export function Basic() {
  return (
    <div class="pt-4 flex min-h-64 w-full justify-center">
      <NavigationMenu aria-label="Example navigation">
        <NavigationMenu.List>
          <NavigationMenu.Item value="products">
            <NavigationMenu.Trigger>Products</NavigationMenu.Trigger>
            <NavigationMenu.Content>
              <div class="gap-2 grid w-[min(28rem,calc(100vw-2rem))] sm:grid-cols-2">
                <NavigationMenu.Link href="#overview" closeOnClick>
                  <span class="font-medium">Overview</span>
                  <span class="text-muted-foreground">Explore the component library.</span>
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#themes" closeOnClick>
                  <span class="font-medium">Themes</span>
                  <span class="text-muted-foreground">Make the library fit your product.</span>
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#accessibility" closeOnClick>
                  <span class="font-medium">Accessibility</span>
                  <span class="text-muted-foreground">Keyboard and screen reader support.</span>
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#getting-started" closeOnClick>
                  <span class="font-medium">Getting started</span>
                  <span class="text-muted-foreground">Install your first component.</span>
                </NavigationMenu.Link>
              </div>
            </NavigationMenu.Content>
          </NavigationMenu.Item>
          <NavigationMenu.Item value="guides">
            <NavigationMenu.Trigger>Guides</NavigationMenu.Trigger>
            <NavigationMenu.Content>
              <div class="w-48">
                <NavigationMenu.Link href="#installation" closeOnClick>
                  Installation
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#composition" closeOnClick>
                  Composition
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#styling" closeOnClick>
                  Styling
                </NavigationMenu.Link>
              </div>
            </NavigationMenu.Content>
          </NavigationMenu.Item>
          <NavigationMenu.Item>
            <NavigationMenu.Link href="#documentation" active>
              Documentation
            </NavigationMenu.Link>
          </NavigationMenu.Item>
        </NavigationMenu.List>
      </NavigationMenu>
    </div>
  )
}
```

### Controlled state and disabled items

Use stable item values with `value` and `onValueChange`. Set the value to `null` to close. Disabled or removed items cannot keep a panel open. `defaultValue` opens an item after hydration.

```tsx
import { Button, NavigationMenu } from 'moraine'
import { createSignal } from 'solid-js'

export function Controlled() {
  const [value, setValue] = createSignal<string | null>(null)
  const [disabled, setDisabled] = createSignal(false)
  return (
    <div class="min-h-56 space-y-4">
      <div class="flex flex-wrap gap-2">
        <Button size="sm" variant="outline" onClick={() => setValue('account')}>
          Open account
        </Button>
        <Button size="sm" variant="outline" onClick={() => setValue(null)}>
          Close
        </Button>
        <Button size="sm" variant="outline" onClick={() => setDisabled((value) => !value)}>
          Toggle disabled
        </Button>
      </div>
      <NavigationMenu
        aria-label="Account navigation"
        value={value()}
        onValueChange={setValue}
        disabled={disabled()}
      >
        <NavigationMenu.List>
          <NavigationMenu.Item value="account">
            <NavigationMenu.Trigger>Account</NavigationMenu.Trigger>
            <NavigationMenu.Content>
              <div class="w-48">
                <NavigationMenu.Link href="#profile" closeOnClick>
                  Profile
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#settings" closeOnClick>
                  Settings
                </NavigationMenu.Link>
              </div>
            </NavigationMenu.Content>
          </NavigationMenu.Item>
          <NavigationMenu.Item value="billing" disabled>
            <NavigationMenu.Trigger>Billing</NavigationMenu.Trigger>
            <NavigationMenu.Content>Billing is unavailable.</NavigationMenu.Content>
          </NavigationMenu.Item>
        </NavigationMenu.List>
      </NavigationMenu>
      <p class="text-xs text-muted-foreground">Open item: {value() ?? 'none'}</p>
    </div>
  )
}
```

### Orientation

Set `orientation="vertical"` for a vertical list. Use `placement="right"` to place its panels beside the triggers.

```tsx
import { NavigationMenu } from 'moraine'

export function Orientation() {
  return (
    <div class="min-h-56 w-full">
      <NavigationMenu aria-label="Vertical navigation" orientation="vertical" placement="right">
        <NavigationMenu.List>
          <NavigationMenu.Item value="products">
            <NavigationMenu.Trigger>Products</NavigationMenu.Trigger>
            <NavigationMenu.Content>
              <div class="w-40">
                <NavigationMenu.Link href="#overview" closeOnClick>
                  Overview
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#features" closeOnClick>
                  Features
                </NavigationMenu.Link>
              </div>
            </NavigationMenu.Content>
          </NavigationMenu.Item>
          <NavigationMenu.Item value="resources">
            <NavigationMenu.Trigger>Resources</NavigationMenu.Trigger>
            <NavigationMenu.Content>
              <div class="w-40">
                <NavigationMenu.Link href="#guides" closeOnClick>
                  Guides
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#support" closeOnClick>
                  Support
                </NavigationMenu.Link>
              </div>
            </NavigationMenu.Content>
          </NavigationMenu.Item>
        </NavigationMenu.List>
      </NavigationMenu>
    </div>
  )
}
```

### RTL

Set `dir="rtl"` on the surrounding container for right-to-left navigation.

```tsx
import { NavigationMenu } from 'moraine'

export function Rtl() {
  return (
    <div dir="rtl" class="min-h-56 w-full">
      <NavigationMenu aria-label="RTL navigation">
        <NavigationMenu.List>
          <NavigationMenu.Item value="products">
            <NavigationMenu.Trigger>المنتجات</NavigationMenu.Trigger>
            <NavigationMenu.Content>
              <div class="w-40">
                <NavigationMenu.Link href="#overview" closeOnClick>
                  نظرة عامة
                </NavigationMenu.Link>
                <NavigationMenu.Link href="#features" closeOnClick>
                  الميزات
                </NavigationMenu.Link>
              </div>
            </NavigationMenu.Content>
          </NavigationMenu.Item>
          <NavigationMenu.Item value="resources">
            <NavigationMenu.Trigger>الموارد</NavigationMenu.Trigger>
            <NavigationMenu.Content>
              <NavigationMenu.Link href="#guides" closeOnClick>
                الأدلة
              </NavigationMenu.Link>
            </NavigationMenu.Content>
          </NavigationMenu.Item>
        </NavigationMenu.List>
      </NavigationMenu>
    </div>
  )
}
```

### Custom links

Use `linkRender` for a router's link component. Forward the supplied attributes, events, children, object style and ref to the anchor. `active` marks the current page with `aria-current="page"`. `closeOnClick` defaults to `false`; enable it when activation should also close the panel. A cancelled click does not request closing.

```tsx
import { A } from '@solidjs/router'
import { NavigationMenu } from 'moraine'
import type { NavigationMenuT } from 'moraine'

const RouterLink = (props: NavigationMenuT.LinkRenderProps) => (
  <A {...props} href={props.href ?? '#'} />
)

export function RouterLinkExample() {
  return (
    <NavigationMenu aria-label="Documentation navigation">
      <NavigationMenu.List>
        <NavigationMenu.Item>
          <NavigationMenu.Link linkRender={RouterLink} href="/components/button">
            Button
          </NavigationMenu.Link>
        </NavigationMenu.Item>
        <NavigationMenu.Item>
          <NavigationMenu.Link linkRender={RouterLink} href="/components/tabs">
            Tabs
          </NavigationMenu.Link>
        </NavigationMenu.Item>
      </NavigationMenu.List>
    </NavigationMenu>
  )
}
```

### Keyboard interaction

Arrow keys along the list axis move focus without opening a panel. Home and End focus the first and last enabled navigation controls. Enter and Space activate a trigger. ArrowDown opens a horizontal menu and enters its content; ArrowRight does the same for a vertical LTR menu, and ArrowLeft for RTL.

Tab and Shift+Tab preserve the order between the trigger, its portaled content and the following navigation control. Arrow keys and Home/End move between panel links, while inputs retain their editing keys. Escape closes the panel and returns focus to its trigger. Outside click and focus close the panel without taking focus away from the destination.

The root renders a navigation landmark. Give it an accessible name when the page has several navigation landmarks. This version supports one level of panels.

## Attributes

| Attributes | Slot | Description |
| --- | --- | --- |
| `data-disabled` | `navigation-menu`, `navigation-menu-item`, `navigation-menu-trigger`, `navigation-menu-link` | Present when the component, slot, or item is disabled. |
| `data-orientation` | `navigation-menu`, `navigation-menu-list`, `navigation-menu-content` | Stores the rendered orientation (horizontal or vertical). |
| `data-expanded` | `navigation-menu-item`, `navigation-menu-trigger`, `navigation-menu-trigger-icon`, `navigation-menu-content` | Present when the panel, accordion, or menu is expanded. |
| `data-closed` | `navigation-menu-content` | Present when disclosure or transition content is closed. |
| `data-active` | `navigation-menu-link` | Present when the item or trigger is active. |

## Props

### NavigationMenu

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| align | 'start' \| 'center' \| 'end' \| undefined | center | Alignment relative to the trigger. |
| closeDelay | number \| undefined | 50 | Delay after the pointer leaves the menu, in milliseconds. |
| defaultValue | string \| null \| undefined | null | Initial open item value. |
| disabled | boolean \| undefined | false | Disable navigation interactions. |
| flip | boolean \| string \| undefined | true | Whether to flip placement when the preferred side lacks space, or a space-delimited fallback placement list. |
| gutter | number \| undefined | 8 | Gap between the trigger and panel, in pixels. |
| onValueChange | ((value: string \| null) => void) \| undefined | — | Called when an interaction requests a different open item. |
| openDelay | number \| undefined | 50 | Hover delay before the first panel opens, in milliseconds. |
| orientation | 'horizontal' \| 'vertical' \| undefined | 'horizontal' | Axis of the top-level navigation list. |
| overflowPadding | number \| undefined | 4 | Padding in pixels between positioned content and the viewport boundary. |
| placement | 'top' \| 'right' \| 'bottom' \| 'left' \| undefined | 'bottom' | Preferred content placement relative to the trigger. |
| shift | number \| undefined | 0 | Cross-axis offset in pixels from the resolved placement. |
| slide | boolean \| undefined | true | Whether the content may slide along its main axis to remain visible. |
| value | string \| null \| undefined | — | Open item value. null closes the menu. |
| children | JSX.Element \| undefined | — | Composed navigation parts. |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| classes | Classes \| undefined | — | Family slot class defaults for this instance. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
| styles | Styles \| undefined | — | Family slot style defaults for this instance. |

### NavigationMenu.List

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| classes | Pick<Classes, 'list'> \| undefined | — | Family slot class defaults for this instance. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
| styles | Pick<Styles, 'list'> \| undefined | — | Family slot style defaults for this instance. |

### NavigationMenu.Item

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| disabled | boolean \| undefined | false | Disable this item's trigger or direct links. |
| value | string \| undefined | — | Unique item value. Generated when omitted; set explicitly for controlled usage. |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| classes | Pick<Classes, 'item'> \| undefined | — | Family slot class defaults for this instance. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
| styles | Pick<Styles, 'item'> \| undefined | — | Family slot style defaults for this instance. |

### NavigationMenu.Trigger

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| classes | Pick<Classes, 'trigger' \| 'triggerIcon'> \| undefined | — | Family slot class defaults for this instance. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
| styles | Pick<Styles, 'trigger' \| 'triggerIcon'> \| undefined | — | Family slot style defaults for this instance. |

### NavigationMenu.Content

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| children | JSX.Element \| undefined | — | Lazily mounted panel content. |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| classes | Pick<Classes, 'content'> \| undefined | — | Family slot class defaults for this instance. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
| styles | Pick<Styles, 'content'> \| undefined | — | Family slot style defaults for this instance. |

### NavigationMenu.Link

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| active | boolean \| undefined | false | Mark this link as the current page. |
| closeOnClick | boolean \| undefined | false | Close the panel after an uncancelled link click. |
| disabled | boolean \| undefined | false | Disable navigation from this link. |
| linkRender | Component<LinkRenderProps> \| undefined | — | Custom link component. Forward all supplied props to its anchor. |
| children | JSX.Element \| undefined | — | — |
| class | SlotClassValue \| undefined | — | Class applied to the component root or trigger element. |
| classes | Pick<Classes, 'link'> \| undefined | — | Family slot class defaults for this instance. |
| style | SlotStyleValue \| undefined | — | Style applied to the component root or trigger element. |
| styles | Pick<Styles, 'link'> \| undefined | — | Family slot style defaults for this instance. |
