# Navigation Menu

A horizontal row of triggers and links that open portaled content panels for site navigation.

## Installation

```tsx
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuContentList,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuLinkDescription,
  NavigationMenuLinkTitle,
  NavigationMenuList,
  NavigationMenuPanel,
  NavigationMenuTrigger,
  NavigationMenuViewport,
} from "@makeplane/propel/components/navigation-menu";
```
## Usage

```tsx
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuContentList,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuLinkDescription,
  NavigationMenuLinkTitle,
  NavigationMenuList,
  NavigationMenuPanel,
  NavigationMenuTrigger,
  NavigationMenuViewport,
} from "@makeplane/propel/components/navigation-menu";

const PRODUCT_LINKS = [
  { href: "#issues", title: "Issues", description: "Track work items across projects." },
  { href: "#cycles", title: "Cycles", description: "Time-boxed sprints for your team." },
  { href: "#modules", title: "Modules", description: "Group related work into modules." },
  { href: "#pages", title: "Pages", description: "Rich documents that live with your work." },
];

const RESOURCE_LINKS = [
  { href: "#docs", title: "Documentation", description: "Guides and API references." },
  { href: "#changelog", title: "Changelog", description: "What shipped recently." },
];

export default function BasicDemo() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger label="Product" />
          <NavigationMenuContent>
            <NavigationMenuContentList>
              {PRODUCT_LINKS.map((item) => (
                <li key={item.href}>
                  <NavigationMenuLink href={item.href} appearance="card">
                    <NavigationMenuLinkTitle>{item.title}</NavigationMenuLinkTitle>
                    <NavigationMenuLinkDescription>
                      {item.description}
                    </NavigationMenuLinkDescription>
                  </NavigationMenuLink>
                </li>
              ))}
            </NavigationMenuContentList>
          </NavigationMenuContent>
        </NavigationMenuItem>

        <NavigationMenuItem>
          <NavigationMenuTrigger label="Resources" />
          <NavigationMenuContent>
            <NavigationMenuContentList>
              {RESOURCE_LINKS.map((item) => (
                <li key={item.href}>
                  <NavigationMenuLink href={item.href} appearance="card">
                    <NavigationMenuLinkTitle>{item.title}</NavigationMenuLinkTitle>
                    <NavigationMenuLinkDescription>
                      {item.description}
                    </NavigationMenuLinkDescription>
                  </NavigationMenuLink>
                </li>
              ))}
            </NavigationMenuContentList>
          </NavigationMenuContent>
        </NavigationMenuItem>

        <NavigationMenuItem>
          <NavigationMenuLink href="#pricing" appearance="item">
            Pricing
          </NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenuList>

      <NavigationMenuPanel>
        <NavigationMenuViewport />
      </NavigationMenuPanel>
    </NavigationMenu>
  );
}
```
## Examples

### Presentations

A link's `appearance` sets its shape: `item` is a top-level pill beside the triggers, while `card` stacks a title over an optional description inside a content panel.

```tsx
import {
  NavigationMenu,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuLinkDescription,
  NavigationMenuLinkTitle,
  NavigationMenuList,
} from "@makeplane/propel/components/navigation-menu";

export default function PresentationsDemo() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuLink href="#pricing" appearance="item">
            Pricing
          </NavigationMenuLink>
        </NavigationMenuItem>
        <NavigationMenuItem>
          <NavigationMenuLink href="#docs" appearance="card">
            <NavigationMenuLinkTitle>Documentation</NavigationMenuLinkTitle>
            <NavigationMenuLinkDescription>
              Guides and API references.
            </NavigationMenuLinkDescription>
          </NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenuList>
    </NavigationMenu>
  );
}
```

### Large content

Cap the content region's height and wrap the link list in a `ScrollArea` so the panel keeps a stable size while long menus scroll.

```tsx
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuContentList,
  NavigationMenuItem,
  NavigationMenuLink,
  NavigationMenuLinkTitle,
  NavigationMenuList,
  NavigationMenuPanel,
  NavigationMenuTrigger,
  NavigationMenuViewport,
} from "@makeplane/propel/components/navigation-menu";
import { ScrollArea } from "@makeplane/propel/components/scroll-area";

const PROJECT_LINKS = Array.from({ length: 24 }, (_, index) => ({
  href: `#project-${index + 1}`,
  title: `Project ${index + 1}`,
}));

export default function LargeContentDemo() {
  return (
    <NavigationMenu>
      <NavigationMenuList>
        <NavigationMenuItem>
          <NavigationMenuTrigger label="Projects" />
          <NavigationMenuContent>
            <div className="flex h-72 flex-col">
              <ScrollArea orientation="vertical">
                <NavigationMenuContentList>
                  {PROJECT_LINKS.map((item) => (
                    <li key={item.href}>
                      <NavigationMenuLink href={item.href} appearance="card">
                        <NavigationMenuLinkTitle>{item.title}</NavigationMenuLinkTitle>
                      </NavigationMenuLink>
                    </li>
                  ))}
                </NavigationMenuContentList>
              </ScrollArea>
            </div>
          </NavigationMenuContent>
        </NavigationMenuItem>

        <NavigationMenuItem>
          <NavigationMenuLink href="#pricing" appearance="item">
            Pricing
          </NavigationMenuLink>
        </NavigationMenuItem>
      </NavigationMenuList>

      <NavigationMenuPanel>
        <NavigationMenuViewport />
      </NavigationMenuPanel>
    </NavigationMenu>
  );
}
```
## API Reference

### NavigationMenu

The navigation menu Root — Base UI's context/state provider. It renders Base UI's unstyled
`<nav>` container (a `<div>` when nested) and carries no propel styling, so it is a behavior-only
role and lives in `components` (rules 1a, 2); the styled parts live in `elements/navigation-menu`
and are grafted onto Base UI behavior here.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, NavigationMenuRootState>` | — | 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

The ready-made trigger row: grafts Base UI's `NavigationMenu.Trigger` behavior onto the styled
trigger, wraps the label, and bakes the rotating disclosure caret (`NavigationMenuIcon` with a
default chevron — defaults are a `components` concern, rule 2a). Override the glyph via `icon`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` | — | Visible trigger label. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, NavigationMenuTriggerState>` | — | 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. |
| `icon` | `ReactNode` | — | Replaces the default disclosure caret (a down chevron) in the trigger's `Icon` slot. |

### NavigationMenuLink

The ready-made navigable link: grafts Base UI's `NavigationMenu.Link` behavior onto the styled
link. `appearance="item"` is a top-level pill beside the triggers; `appearance="card"` is a
stacked entry inside a `NavigationMenuContent`, pairing a `NavigationMenuLinkTitle` with an
optional `NavigationMenuLinkDescription`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `appearance` (required) | `"item" \| "card"` | — |  |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<DetailedHTMLProps<AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, NavigationMenuLinkState>` | — | 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. |

### NavigationMenuPanel

The navigation menu surface: Base UI portal + positioner + popup grafted onto Propel styling.
Named `Panel` to avoid clashing with Base UI's atomic per-item `NavigationMenu.Content`. The
viewport and per-item content remain children.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | 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` | `"top" \| "bottom" \| "left" \| "right" \| "inline-end" \| "inline-start"` | `bottom` | Which side of the trigger the panel opens toward. |
| `sideOffset` | `number \| OffsetFunction` | `4` | Distance in px between the trigger and the panel. |
| `align` | `"center" \| "start" \| "end"` | `start` | Alignment of the panel relative to the trigger along `side`. |
| `alignOffset` | `number \| OffsetFunction` | `0` | Additional offset in px along the align axis — slides the panel along the trigger's edge without changing `align`. |
| `collisionPadding` | `Padding` | `5` | Minimum gap in px between the panel and the viewport edge it would otherwise touch. |
| `collisionBoundary` | `Boundary` | — | The element or rect the panel is confined to when avoiding collisions. Base UI's clipping ancestors when unset. |
| `collisionAvoidance` | `CollisionAvoidance` | — | What to do when the panel would overflow the boundary, set per axis: flip to the other side, shift along it, or stay put. Base UI flips the side and shifts the alignment when unset. |
| `sticky` | `boolean` | `false` | Keep the panel on screen after the trigger scrolls out of view, instead of following it out. |
| `positionMethod` | `"absolute" \| "fixed"` | `"absolute"` | Which CSS `position` the positioner uses. `fixed` escapes overflow-clipping ancestors, e.g. a menubar inside a sticky header. |
| `anchor` | `Element \| VirtualElement \| RefObject<Element \| null> \| (() => Element \| VirtualElement \| null) \| null` | — | Position against this element instead of the trigger — an element, a ref, a virtual element, or a function returning one. Geometry only; the trigger keeps its role and interactions. |
| `disableAnchorTracking` | `boolean` | `false` | Stop tracking layout shifts of the anchor — position once, on open. |
