# Navigation Menu

Site navigation links, with panels that open from the bar.

Source: [navigation-menu.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/navigation-menu.tsx)

```tsx
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
  navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";
import { preventNavigation } from "@/examples/data";

const products = [
  { title: "Projects", description: "Plan work in lists, boards and timelines." },
  { title: "Docs", description: "Write specs and notes next to the work." },
  { title: "Insights", description: "Track progress across every team." },
  { title: "Automations", description: "Hand repetitive steps to rules." },
];

const resources = ["Guides", "Changelog", "Community", "Support"];

export default function NavigationMenuBasic() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Product</NavigationMenuTrigger>
          <NavigationMenuContent>
            <ul className="grid w-[30rem] max-w-full grid-cols-1 sm:grid-cols-2">
              {products.map((product) => (
                <li key={product.title}>
                  <NavigationMenuLink href="#" onClick={preventNavigation}>
                    <span className="font-medium">{product.title}</span>
                    <span className="text-muted-foreground">{product.description}</span>
                  </NavigationMenuLink>
                </li>
              ))}
            </ul>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Resources</NavigationMenuTrigger>
          <NavigationMenuContent>
            <ul className="flex w-48 flex-col">
              {resources.map((resource) => (
                <li key={resource}>
                  <NavigationMenuLink href="#" onClick={preventNavigation}>
                    {resource}
                  </NavigationMenuLink>
                </li>
              ))}
            </ul>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuLink
            href="#"
            onClick={preventNavigation}
            className={navigationMenuTriggerStyle()}
          >
            Pricing
          </NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/navigation-menu
```

## Usage

```tsx
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
  navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";

export function SiteNavigation() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Product</NavigationMenuTrigger>
          <NavigationMenuContent>
            <NavigationMenuLink href="/projects">Projects</NavigationMenuLink>
            <NavigationMenuLink href="/docs">Docs</NavigationMenuLink>
          </NavigationMenuContent>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuLink href="/pricing" className={navigationMenuTriggerStyle()}>
            Pricing
          </NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}
```

Each `NavigationMenuItem` holds either a link or a trigger with content. Use
`navigationMenuTriggerStyle()` to make a plain link match the triggers.

For menus of actions rather than links, use a [Dropdown Menu](https://ui.jarv.is/components/dropdown-menu.md) or a
[Menubar](https://ui.jarv.is/components/menubar.md).

## Composition

- `NavigationMenu`
  - `NavigationMenuList`
    - `NavigationMenuItem`
      - `NavigationMenuTrigger`
      - `NavigationMenuContent`
        - `NavigationMenuLink`

## Examples

### With icons

```tsx
import {
  BookOpenIcon,
  CodeIcon,
  LifeBuoyIcon,
  NewspaperIcon,
  PaletteIcon,
  ZapIcon,
} from "lucide-react";

import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
} from "@/components/ui/navigation-menu";
import { preventNavigation } from "@/examples/data";

const developers = [
  { title: "API reference", description: "Every endpoint, with examples.", icon: CodeIcon },
  { title: "Webhooks", description: "React to changes as they happen.", icon: ZapIcon },
  { title: "Theming", description: "Match the embed to your brand.", icon: PaletteIcon },
];

const learn = [
  { title: "Guides", description: "Step-by-step walkthroughs.", icon: BookOpenIcon },
  { title: "Blog", description: "News and stories from the team.", icon: NewspaperIcon },
  { title: "Support", description: "Talk to a person.", icon: LifeBuoyIcon },
];

const sections = [
  { label: "Developers", links: developers },
  { label: "Learn", links: learn },
];

export default function NavigationMenuWithIcons() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        {sections.map((section) => (
          <NavigationMenuItem key={section.label}>
            <NavigationMenuTrigger>{section.label}</NavigationMenuTrigger>
            <NavigationMenuContent>
              <ul className="flex w-72 flex-col">
                {section.links.map(({ title, description, icon: Icon }) => (
                  <li key={title}>
                    <NavigationMenuLink
                      href="#"
                      onClick={preventNavigation}
                      className="flex-row items-start gap-2.5"
                    >
                      <Icon className="mt-0.5" />
                      <span className="flex flex-col gap-0.5">
                        <span className="font-medium">{title}</span>
                        <span className="text-muted-foreground">{description}</span>
                      </span>
                    </NavigationMenuLink>
                  </li>
                ))}
              </ul>
            </NavigationMenuContent>
          </NavigationMenuItem>
        ))}
      </NavigationMenuList>
    </NavigationMenu>
  );
}
```

### Active link

Set `active` on the link to the current page.

```tsx
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuList,
  NavigationMenuTrigger,
  navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";
import { preventNavigation } from "@/examples/data";

const settings = ["Profile", "Security", "Notifications", "Billing"];

export default function NavigationMenuActiveLink() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuLink
            href="#"
            onClick={preventNavigation}
            className={navigationMenuTriggerStyle()}
          >
            Overview
          </NavigationMenuLink>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuTrigger>Settings</NavigationMenuTrigger>
          <NavigationMenuContent>
            <ul className="flex w-44 flex-col">
              {settings.map((page) => (
                <li key={page}>
                  <NavigationMenuLink
                    href="#"
                    onClick={preventNavigation}
                    active={page === "Security"}
                  >
                    {page}
                  </NavigationMenuLink>
                </li>
              ))}
            </ul>
          </NavigationMenuContent>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}
```

### Client-side routing

Pass your router's link to `render`.

```tsx
import { Link } from "@tanstack/react-router";

import { NavigationMenuLink } from "@/components/ui/navigation-menu";

export function DocsLink() {
  return <NavigationMenuLink render={<Link to="/docs" />}>Docs</NavigationMenuLink>;
}
```

## Props

`NavigationMenu` also takes `side`, `sideOffset`, `align` and `alignOffset` to position the panel.

| Part | Extends |
| --- | --- |
| `NavigationMenu` | [`NavigationMenu.Root`](https://base-ui.com/react/components/navigation-menu#root) |
| `NavigationMenuList` | [`NavigationMenu.List`](https://base-ui.com/react/components/navigation-menu#list) |
| `NavigationMenuItem` | [`NavigationMenu.Item`](https://base-ui.com/react/components/navigation-menu#item) |
| `NavigationMenuTrigger` | [`NavigationMenu.Trigger`](https://base-ui.com/react/components/navigation-menu#trigger) |
| `NavigationMenuContent` | [`NavigationMenu.Content`](https://base-ui.com/react/components/navigation-menu#content) |
| `NavigationMenuLink` | [`NavigationMenu.Link`](https://base-ui.com/react/components/navigation-menu#link) |

The tables leave out `className`, `style` and `render`. Parts built on Base UI take them as its [styling](https://base-ui.com/react/handbook/styling) and [composition](https://base-ui.com/react/handbook/composition) guides describe.

### NavigationMenu

| Prop                    | Type                                                                           | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ----------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `actionsRef?`           | `React.RefObject<NavigationMenuRootActions \| null>`                           | 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.                                                                                                                                                                                                                                          |
| `onOpenChangeComplete?` | `((open: boolean) => void)`                                                    | Event handler called after any animations complete when the navigation menu is closed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `value?`                | `any`                                                                          | 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. Default: `null`                                                                                                                                                                                                                                                                                                                                                                                                   |
| `defaultValue?`         | `any`                                                                          | The uncontrolled value of the item that should be initially selected. To render a controlled navigation menu, use the `value` prop instead. Default: `null`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `onValueChange?`        | `((value: any, eventDetails: NavigationMenu.Root.ChangeEventDetails) => void)` | Callback fired when the value changes.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `delay?`                | `number`                                                                       | How long to wait before opening the navigation popup. Specified in milliseconds. Default: `50`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `closeDelay?`           | `number`                                                                       | How long to wait before closing the navigation popup. Specified in milliseconds. Default: `50`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `orientation?`          | `"horizontal" \| "vertical"`                                                   | The orientation of the navigation menu. Default: `'horizontal'`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `side?`                 | `"bottom" \| "inline-end" \| "inline-start" \| "left" \| "right" \| "top"`     | Which side of the anchor element to align the popup against. May automatically change to avoid collisions. Default: `"bottom"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `sideOffset?`           | `number \| OffsetFunction`                                                     | 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. Default: `4` |
| `align?`                | `"center" \| "end" \| "start"`                                                 | How to align the popup relative to the specified side. Default: `"start"`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `alignOffset?`          | `number \| OffsetFunction`                                                     | 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. Default: `0`  |

### NavigationMenuItem

| Prop     | Type  | Description                                                                                                                                                                                |
| -------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `value?` | `any` | 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. |

### NavigationMenuTrigger

| Prop            | Type      | Description                                                                                                                                                                                       |
| --------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `disabled?`     | `boolean` | Whether the component should ignore user interaction. Default: `false`                                                                                                                            |
| `nativeButton?` | `boolean` | 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>`). Default: `true` |

### NavigationMenuContent

| Prop           | Type      | Description                                                                                                                                                              |
| -------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `keepMounted?` | `boolean` | 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. Default: `false` |

### NavigationMenuLink

| Prop            | Type      | Description                                                                     |
| --------------- | --------- | ------------------------------------------------------------------------------- |
| `active?`       | `boolean` | Whether the link is the currently active page. Default: `false`                 |
| `closeOnClick?` | `boolean` | Whether to close the navigation menu when the link is clicked. Default: `false` |
