Navigation menu
Site navigation with rich dropdowns, wrapping the Base UI Navigation Menu. One shared popup morphs between the open item's content instead of popping a new one per item, and it's built for links: app actions belong in the Menu.
Playground
Every control maps to a prop. Hover between the items and watch the popup resize; the Code tab always shows the markup for exactly what you've configured.
Nested submenus
Open Product, then hover Integrations: a row with a chevron opens a second panel beside the first. That's a NavigationMenuRoot nested inside a NavigationMenuContent; its trigger reads as a row, and its content opens in a flyout with side="inline-end".
Nested inline submenus
Open Solutions: a list of audiences sits on the left and the chosen one's details on the right, swapping in place as you move down the list, with no second popup. That's a nested root with inline plus a NavigationMenuViewport beside its list and a defaultValue, so the selected item's content renders there instead of a flyout.
States
Closed and open are live, so hover or click a trigger. While open, the trigger carries data-popup-open (the chevron flips on it) and the incoming content carries data-activation-direction, left or right depending on which neighbor was open before; an item that is only a link has no open state at all.
Styling states
Triggers carry data-popup-open (the built-in chevron flips on it); the popup's size animates to the measured --popup-width/--popup-height, and content slides by data-activation-direction:
.ub-navigation-menu-popup {
transition-duration: var(--ub-duration-slow);
}Props
The root bundles the popup machinery, so the parts you write are the list and its items. The essentials:
| Prop | Type | Default | Description |
|---|---|---|---|
NavigationMenuRoot.delay | number | 50 | Milliseconds before opening on hover (closeDelay mirrors it). |
NavigationMenuRoot.orientation | "horizontal" | "vertical" | "horizontal" | Vertical stacks the list and swaps the arrow keys. |
NavigationMenuRoot.side | PositionerSide | โ | Which side of the trigger the popup opens on; nested menus usually want inline-end. Also align. |
NavigationMenuRoot.sideOffset | number | 8 | Gap between the trigger and the popup, in pixels. |
NavigationMenuRoot.inline | boolean | false | Renders children without the bundled popup; pair with a NavigationMenuViewport for same-panel submenus. |
NavigationMenuTrigger.children | ReactNode | โ | The item's label; the chevron renders automatically. |
NavigationMenuLink.href | string | โ | A real anchor; render={<Link />} plugs in a router. |
Agent instructions
Working with a coding agent? Paste this into its context, or point it at llms.txt for the whole library.
NavigationMenuRoot, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuViewport, from @usebones/react.
- Structure: NavigationMenuRoot wraps NavigationMenuList of NavigationMenuItems. An item holds a NavigationMenuTrigger (label as children; chevron automatic) + NavigationMenuContent, or just a NavigationMenuLink for a plain link.
- The popup machinery is bundled in the root; one shared popup morphs between the open item's content.
- Nested submenus: put another NavigationMenuRoot inside a NavigationMenuContent (side="inline-end" opens the flyout beside the panel). Same-panel submenus: nested root with inline plus a NavigationMenuViewport next to its list and a defaultValue.
- Links are real anchors: pass href, or render={<Link to="..." />} for a router. Fill content with NavigationMenuLinks in your own layout.
- For links only; app command surfaces use Menu.
- Restyle in CSS via .ub-navigation-menu-trigger, .ub-navigation-menu-link, .ub-navigation-menu-popup, [data-popup-open], [data-activation-direction]. Size morph runs on --popup-width/--popup-height; keep those transitions. Tokens only.