---
id: "packages/active-ui/navigation-menu"
title: "NavigationMenu"
description: "The main navigation of a site, with sections that open a panel of links below the bar."
url: "/packages/active-ui/navigation-menu"
locale: "en"
revision: "fd8c4105c6d4e31a"
---

# NavigationMenu

A navigation menu is the top navigation of a website: a bar of sections, each opening a panel with
its links, and plain links beside them. It is for moving between pages. For commands, use a
[menubar](/packages/active-ui/menubar) or a [dropdown menu](/packages/active-ui/dropdown-menu);
for the navigation of an application, a [sidebar](/packages/active-ui/sidebar).

**Example: Sections with panels, and a plain link**

Client: Runs entirely in your browser. Source: `examples/ui/navigation-menu.tsx`.

```tsx
// apps/docs/examples/ui/navigation-menu.tsx
'use client';

import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
} from '@workspace/active-ui/components/primitives/navigation-menu';

// Invented sections; the links point to anchors, nothing navigates.
const SECTIONS = [
  {
    trigger: 'Pension',
    links: [
      {title: 'Participants', description: 'Active and deferred members.'},
      {title: 'Schemes', description: 'Accrual rules per scheme.'},
      {title: 'Calculations', description: 'Batches and their results.'},
    ],
  },
  {
    trigger: 'Relations',
    links: [
      {title: 'Persons', description: 'The register of people.'},
      {title: 'Organisations', description: 'Employers and providers.'},
    ],
  },
] as const;

const anchor = (title: string) =>
  `#nav-${title.toLowerCase().replaceAll(' ', '-')}`;

/**
 * The main navigation of a site: sections whose links open in a panel below
 * the bar, and a plain link beside them. Moving from one section to the
 * next keeps the panel open and slides it to the new content.
 */
export default function NavigationMenuExample() {
  return (
    <NavigationMenu aria-label="Main">
      <NavigationMenuList>
        {SECTIONS.map((section) => (
          <NavigationMenuItem key={section.trigger}>
            <NavigationMenuTrigger>{section.trigger}</NavigationMenuTrigger>
            <NavigationMenuContent>
              <ul className="grid w-72 gap-1">
                {section.links.map((link) => (
                  <li key={link.title}>
                    <NavigationMenuLink
                      href={anchor(link.title)}
                      className="flex-col items-start gap-0.5"
                    >
                      <span className="font-medium">{link.title}</span>
                      <span className="text-muted-foreground">
                        {link.description}
                      </span>
                    </NavigationMenuLink>
                  </li>
                ))}
              </ul>
            </NavigationMenuContent>
          </NavigationMenuItem>
        ))}
        <NavigationMenuItem>
          <NavigationMenuLink href={anchor('Reports')}>
            Reports
          </NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}
```

## Usage

```tsx
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
} from '@workspace/active-ui/components/primitives/navigation-menu';

<NavigationMenu aria-label="Main">
  <NavigationMenuList>
    <NavigationMenuItem>
      <NavigationMenuTrigger>Pension</NavigationMenuTrigger>
      <NavigationMenuContent>
        <NavigationMenuLink href="/participants">
          Participants
        </NavigationMenuLink>
      </NavigationMenuContent>
    </NavigationMenuItem>
  </NavigationMenuList>
</NavigationMenu>;
```

## Anatomy

- `NavigationMenu`: the `<nav>`. `align` (default `start`) places the panel under its trigger.
  It renders the panel's positioner itself.
- `NavigationMenuList` with a `NavigationMenuItem` per section or link.
- `NavigationMenuTrigger` and `NavigationMenuContent`: a section and its panel. Moving between
  triggers with a panel open slides the panel to the new content.
- `NavigationMenuLink`: a link, in a panel or directly in the list. `render` makes your router's
  link the element. `navigationMenuTriggerStyle()` gives another element the look of a trigger.
- `NavigationMenuPositioner`: the positioner of the panel, which `NavigationMenu` renders itself.
- `NavigationMenuIndicator`: an optional pointer, a small rotated square, for a trigger of your own
  design; the menu does not render it.

## What your application provides

- A name for the `<nav>` (`aria-label`), the sections and their links.
- The current page: give its link `active`, which marks it (`data-active`).

## Accessibility and keyboard

- `Tab` moves through the triggers and links; `Enter`, `Space` or `↓` opens a panel and `Tab`
  continues into its links.
- `Escape` closes the panel and returns the focus to its trigger.
- On a small screen, a bar of panels needs room it does not have: offer the same links in a
  [sheet](/packages/active-ui/sheet) or a [drawer](/packages/active-ui/drawer) instead.

## API

### NavigationMenu

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `actionsRef` | `RefObject<NavigationMenuRootActions \| null>` | no | A ref to imperative actions. - `unmount`: Ends the closing phase of the navigation menu popup after an externally controlled closing animation finishes. Call `preventUnmountOnClose()` in `onValueChange` first, otherwise the navigation menu popup completes closing on its own. Whether it leaves the DOM is decided by `keepMounted` on the portal. - `close`: Closes the navigation menu imperatively when called. |
| `align` | `Align` | no | How to align the popup relative to the specified side. |
| `closeDelay` | `number` | no | How long to wait before closing the navigation popup. Specified in milliseconds. |
| `defaultValue` | `any` | no | The uncontrolled value of the item that should be initially selected.  To render a controlled navigation menu, use the `value` prop instead. |
| `delay` | `number` | no | How long to wait before opening the navigation popup. Specified in milliseconds. |
| `onOpenChangeComplete` | `((open: boolean) => void)` | no | Event handler called after any animations complete when the navigation menu is closed. |
| `onValueChange` | `((value: any, eventDetails: NavigationMenuRoot.ChangeEventDetails) => void)` | no | Callback fired when the value changes. |
| `orientation` | `"horizontal" \| "vertical"` | no | The orientation of the navigation menu. |
| `render` | `ComponentRenderFn<HTMLProps, NavigationMenuRootState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| `value` | `any` | no | The controlled value of the navigation menu item that should be currently open. When non-nullish, the menu will be open. When nullish, the menu will be closed.  To render an uncontrolled navigation menu, use the `defaultValue` prop instead. |

### Other parts

**NavigationMenuContent**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `keepMounted` | `boolean` | no | Whether to keep the content mounted in the DOM while the popup is closed. Ensures the content is present during server-side rendering for web crawlers. |
| `render` | `ComponentRenderFn<HTMLProps, NavigationMenuContentState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**NavigationMenuIndicator**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, NavigationMenuIconState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**NavigationMenuItem**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, NavigationMenuItemState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| `value` | `any` | no | A unique value that identifies this navigation menu item. If no value is provided, a unique ID will be generated automatically. Use when controlling the navigation menu programmatically. |

**NavigationMenuLink**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `active` | `boolean` | no | Whether the link is the currently active page. |
| `closeOnClick` | `boolean` | no | Whether to close the navigation menu when the link is clicked. |
| `render` | `ComponentRenderFn<DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, NavigationMenuLinkState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**NavigationMenuList**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `render` | `ComponentRenderFn<HTMLProps, NavigationMenuListState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**NavigationMenuTrigger**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | no | Whether the component should ignore user interaction. |
| `nativeButton` | `boolean` | no | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). |
| `render` | `ComponentRenderFn<HTMLProps, NavigationMenuTriggerState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

**NavigationMenuPositioner**

| Prop | Type | Required | Description |
| --- | --- | --- | --- |
| `align` | `Align` | no | How to align the popup relative to the specified side. |
| `alignOffset` | `number \| OffsetFunction` | no | Additional offset along the alignment axis in pixels. Also accepts a function that returns the offset to read the dimensions of the anchor and positioner elements, along with its side and alignment.  The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side. |
| `anchor` | `Element \| RefObject<Element \| null> \| VirtualElement \| (() => Element \| VirtualElement \| null) \| null` | no | An element to position the popup against. By default, the popup will be positioned against the trigger. |
| `arrowPadding` | `number` | no | Minimum distance to maintain between the arrow and the edges of the popup.  Use it to prevent the arrow element from hanging out of the rounded corners of a popup. |
| `collisionAvoidance` | `CollisionAvoidance` | no | Determines how to handle collisions when positioning the popup.  `side` controls overflow on the preferred placement axis (`top`/`bottom` or `left`/`right`): - `'flip'`: keep the requested side when it fits; otherwise try the opposite side (`top` and `bottom`, or `left` and `right`). - `'shift'`: never change side; keep the requested side and move the popup within the clipping boundary so it stays visible. - `'none'`: do not correct side-axis overflow.  `align` controls overflow on the alignment axis (`start`/`center`/`end`): - `'flip'`: keep side, but swap `start` and `end` when the requested alignment overflows. - `'shift'`: keep side and requested alignment, then nudge the popup along the alignment axis to fit. - `'none'`: do not correct alignment-axis overflow.  `fallbackAxisSide` controls fallback behavior on the perpendicular axis when the preferred axis cannot fit: - `'start'`: allow perpendicular fallback and try the logical start side first (`top` before `bottom`, or `left` before `right` in LTR). - `'end'`: allow perpendicular fallback and try the logical end side first (`bottom` before `top`, or `right` before `left` in LTR). - `'none'`: do not fallback to the perpendicular axis.  When `side` is `'shift'`, explicitly setting `align` only supports `'shift'` or `'none'`. If `align` is omitted, it defaults to `'flip'`. |
| `collisionBoundary` | `Boundary` | no | An element or a rectangle that delimits the area that the popup is confined to. |
| `collisionPadding` | `Padding` | no | Additional space to maintain from the edge of the collision boundary. |
| `disableAnchorTracking` | `boolean` | no | Whether to disable the popup from tracking any layout shift of its positioning anchor. |
| `positionMethod` | `"absolute" \| "fixed"` | no | Determines which CSS `position` property to use. |
| `render` | `ComponentRenderFn<HTMLProps, NavigationMenuPositionerState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` | no | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| `side` | `Side` | no | Which side of the anchor element to align the popup against. May automatically change to avoid collisions. |
| `sideOffset` | `number \| OffsetFunction` | no | Distance between the anchor and the popup in pixels. Also accepts a function that returns the distance to read the dimensions of the anchor and positioner elements, along with its side and alignment.  The function takes a `data` object parameter with the following properties: - `data.anchor`: the dimensions of the anchor element with properties `width` and `height`. - `data.positioner`: the dimensions of the positioner element with properties `width` and `height`. - `data.side`: which side of the anchor element the positioner is aligned against. - `data.align`: how the positioner is aligned relative to the specified side. |
| `sticky` | `boolean` | no | Whether to maintain the popup in the viewport after the anchor element was scrolled out of view. |

**navigationMenuTriggerStyle**: `(props?: ClassProp \| undefined) => string`

> [!NOTE]
> **Based on shadcn/ui**
>
> This page adapts the Navigation Menu documentation of shadcn/ui (MIT licence)
> to this package; see [Third-party notices](/third-party-notices).
