Navigation Menu
NavigationMenu is a website header menu: links and triggers that open dropdown panels (mega menus).
<nav>, triggers with aria-expanded, and aria-current="page" on the current page link (the active prop). Hovering opens a panel after a short delay so it doesn't open by accident, and moving to another trigger skips the delay; activationMode="manual" opens on click only. Keyboard: Tab moves through the items, Enter / Space opens, left / right arrows move between items, Tab enters the panel, Esc closes it and returns focus to the trigger. Panels sit in one floating viewport that resizes to the content (Motion tokens, off with reduced motion); RTL follows YugenProvider. Links in a panel take icon and description; asChild uses your router's Link. On narrow screens, put the same links in a Drawer, like the AppShell sidebar.Import
Install from npm, copy the source into your project with @yugen-ui/cli or the shadcn CLI, or import directly when working in this repo.
// npm install yugen-uiimport { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger, NavigationMenuContent, NavigationMenuLink, NavigationMenuIndicator,} from "yugen-ui";Variants
Each tile is a standalone example: hover to copy, or press Code to see the source.
Narrow screens: Drawer
A container query hides the menu in a narrow header; a button opens a Drawer with the same links in a Nav.
Router links (asChild) & current page
active sets aria-current="page" and a brand bar under the link.
Open on click only
activationMode="manual": hover and focus don't open panels — suited to touch screens and busy headers.
Usage
- Keep 3–7 top-level items; group subpages in panels with a title and a short description.
- Mark the current page with
activeso it getsaria-current="page". - On narrow screens, move the links into a Drawer instead of shrinking the menu.
- Don't use NavigationMenu for commands (Save, Delete…). Use Menu or Menubar.
- Don't make a trigger a link: pressing it only opens the panel; put the overview page as a link inside the panel.
- Don't use it for in-app navigation with a sidebar. Use Nav / AppShell.
Props
| Prop · Type | Default | Description |
|---|---|---|
| value / defaultValue / onValueChangestring / (value: string) => void | Default: — | Open item (controlled / uncontrolled); "" when closed. |
| activationMode"automatic" | "manual" | Default: "automatic" | automatic: hover / focus opens panels; manual: only a click does. |
| delayDuration / skipDelayDurationnumber | Default: 200 / 300 | Delay (ms) before hover opens a panel; time during which moving to another trigger skips the delay. |
| orientation"horizontal" | "vertical" | Default: "horizontal" | Direction of the list and of the arrow keys. |
| aria-labelstring | Default: "Main" | Label of the <nav> element. |
| viewportClassNamestring | Default: — | Class for the floating viewport that holds the open panel. |
| NavigationMenuItem | Default: — | Item key (generated by default). |
| NavigationMenuLink | Default: — | Current page: aria-current="page" and the selected style. |
| NavigationMenuLink | Default: — | Icon and one-line description (links inside NavigationMenuContent). |
| NavigationMenuLink | Default: — | Uses the child element (your router's Link) as the link. |
| NavigationMenuIndicatorcomponent | Default: — | Small arrow under the open trigger (place it inside NavigationMenuList). |
| navigationMenuTriggerClassstring | Default: — | Class of the top-level trigger / link, for elements you build yourself. |
| refRef<HTMLElement> | Default: — | Points to the <nav> element. |
Messages
Text built into the component: labels, aria-labels, screen reader announcements.
English by default. Change it with the messages prop of YugenProvider, by the namespace below; messagesVi has the Vietnamese set. See Interface language.
messages.navigationMenu
| Key · Type | Default (English) | messagesVi |
|---|---|---|
| labelstring aria-label của thẻ <nav> (khi không truyền | Default (English)"Main" | messagesVi"Điều hướng chính" |