NavMenu
Overview
NavMenu builds site-level horizontal or vertical navigation with a single shared floating surface. It replaces the retired NavigationMenu family (see the NavMenu migration guide) and is modeled directly on the Popper primitives: the whole viewport is one PopperPositioner, its reference switches to the active trigger, and all hover timing runs on a single shared Popper hover machine with value routing through pendingValue.
SNavMenu is a data-driven composite: pass an items array and it renders the nav > ul > li structure, trigger/content pairs, indicator, and the floating viewport. Styles are injected through an scv() recipe.
For an unstyled, data-driven usage, import
@vean/aria/nav-menu.
Features
- Single shared Popper model — one
PopperRoot, onePopperPositioner(the viewport), and a dynamic reference that switches to the active trigger. Positioning (Floating UI), the grace corridor and Escape/outside dismissal come from the positioner for free. - Value routing — hover timing (open delay, skip-delay window) runs on the shared machine; the value of the most recently hovered trigger routes into
modelValue, so switching between triggers is instant. - Hover / click triggers — hover opens and click toggles by default;
disableClickTrigger/disableHoverTriggerturn each trigger mode off independently. - Grace corridor — moving between a trigger and the open viewport is protected by a real geometric corridor (no debounce), with
skipDelayDurationanddisablePointerLeaveClosetuning closing behavior. - Submenu surfaces — items with
childrenopen a floating viewport anchored to their trigger, with an arrow indicator andunmountOnHidecontrol. - Hidden items —
hiddendrops an item and its whole subtree from the root list and from the nested flyouts; an item whose children are all hidden renders as a leaf link. - Keyboard navigation — arrow keys move focus across items;
Enter/Spaceactivate a link or toggle a submenu; the entry arrow key moves focus into the content;Escapecloses and restores focus to the trigger. - Popup arrow — the viewport carries a
PopperArrowthat points at the active trigger; its placement and rotation come from Floating UI’sarrowmiddleware, and it slides with the viewport on trigger switch. - Controlled / uncontrolled — with
modelValue, highlight follows the prop andupdate:modelValuefires; otherwisedefaultValueseeds the initial open item. - Bidirectional —
orientationsupports horizontal / vertical;dirsupports LTR / RTL with logical placement. - Six sizes —
size(xs…2xl) variants cover spacing and type scale across the list, trigger, viewport, and links. - Aria composition —
NavMenuRoot/List/Item/Trigger/Content/Link/Viewportplus theCompactseries are exported from@vean/aria/nav-menu.
Usage
Demos
Basic
Nested
API
Aria Layer
12NavMenu
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<NavMenuUi> | — | Per-slot class overrides for the component. |
items | NavMenuOptionData[] | — | The items to render in the navigation menu. |
modelValue | string | — | The controlled value of the active item. Can be used as `v-model`. |
defaultValue | string | — | The value of the item that should be active when initially rendered. |
dir | Direction | — | The reading direction of the menu. |
orientation | DataOrientation | — | The orientation of the menu. |
delayDuration | number | 200 | The duration from when the pointer enters the trigger until the submenu opens. |
skipDelayDuration | number | 300 | How much time a user has to enter another trigger without incurring a delay again. |
disableClickTrigger | boolean | false | If `true`, the menu cannot be opened by clicking a trigger. |
disableHoverTrigger | boolean | false | If `true`, the menu cannot be opened by hovering a trigger. |
disablePointerLeaveClose | boolean | false | If `true`, the menu will not close when the pointer leaves the content. |
itemProps | NavMenuItemProps | — | Properties forwarded to the item element. |
linkProps | Omit<NavMenuLinkProps, LinkBasePropsKey> | — | Properties forwarded to the link element. |
subTriggerProps | NavMenuSubTriggerProps | — | Properties forwarded to the nested flyout trigger element. |
subContentProps | NavMenuSubContentProps | — | Properties forwarded to the nested flyout content element. |
triggerProps | NavMenuTriggerProps | — | Properties forwarded to the trigger element. |
contentProps | NavMenuContentProps | — | Properties forwarded to the content element. |
listProps | NavMenuListProps | — | Properties forwarded to the list element. |
viewportProps | NavMenuViewportProps | — | Properties forwarded to the viewport element. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | string | Emitted when the model value changes. |
select | CustomEvent<{ originalEvent: Event; }> | Emitted when a link is selected. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | props: { item: NavMenuOptionData; isTrigger?: boolean; } | — |
item-leading | props: { item: NavMenuOptionData; } | — |
item-trailing | props: { item: NavMenuOptionData; } | — |
item-link-icon | props: { item: NavMenuOptionData; } | — |
item-trigger-icon | props: { item: NavMenuOptionData; } | — |
item-children | props: { item: NavMenuOptionData; } | — |
NavMenuCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | NavMenuOptionData[] | — | The items to render in the navigation menu. |
modelValue | string | — | The controlled value of the active item. Can be used as `v-model`. |
defaultValue | string | — | The value of the item that should be active when initially rendered. |
dir | Direction | — | The reading direction of the menu. |
orientation | DataOrientation | — | The orientation of the menu. |
delayDuration | number | 200 | The duration from when the pointer enters the trigger until the submenu opens. |
skipDelayDuration | number | 300 | How much time a user has to enter another trigger without incurring a delay again. |
disableClickTrigger | boolean | false | If `true`, the menu cannot be opened by clicking a trigger. |
disableHoverTrigger | boolean | false | If `true`, the menu cannot be opened by hovering a trigger. |
disablePointerLeaveClose | boolean | false | If `true`, the menu will not close when the pointer leaves the content. |
itemProps | NavMenuItemProps | — | Properties forwarded to the item element. |
linkProps | Omit<NavMenuLinkProps, LinkBasePropsKey> | — | Properties forwarded to the link element. |
subTriggerProps | NavMenuSubTriggerProps | — | Properties forwarded to the nested flyout trigger element. |
subContentProps | NavMenuSubContentProps | — | Properties forwarded to the nested flyout content element. |
triggerProps | NavMenuTriggerProps | — | Properties forwarded to the trigger element. |
contentProps | NavMenuContentProps | — | Properties forwarded to the content element. |
listProps | NavMenuListProps | — | Properties forwarded to the list element. |
viewportProps | NavMenuViewportProps | — | Properties forwarded to the viewport element. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | string | Emitted when the model value changes. |
select | CustomEvent<{ originalEvent: Event; }> | Emitted when a link is selected. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | props: { item: NavMenuOptionData; isTrigger?: boolean; } | — |
item-leading | props: { item: NavMenuOptionData; } | — |
item-trailing | props: { item: NavMenuOptionData; } | — |
item-link-icon | props: { item: NavMenuOptionData; } | — |
item-trigger-icon | props: { item: NavMenuOptionData; } | — |
item-children | props: { item: NavMenuOptionData; } | — |
NavMenuContent
Props
| Prop | Type | Default | Description |
|---|---|---|---|
forceMount | boolean | — | Used to force mounting when more control is needed.
Useful when controlling animation with Vue animation libraries. |
NavMenuItem
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Value associated with the current item. |
NavMenuLink
Props
| Prop | Type | Default | Description |
|---|---|---|---|
selected | boolean | — | Marks the link as the currently selected page. |
sub | boolean | — | Whether the link is rendered inside a submenu. When `true`, the `subLink` UI slot is
used instead of `link` (mirroring the root/sub split of the list). |
replace | boolean | — | Calls `router.replace` instead of `router.push`. |
disabled | boolean | — | When `true`, the link is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
href | string | — | The URL the link should navigate to when clicked on. |
to | string | import("vue-router").RouteLocationAsRelativeGeneric | import("vue-router").RouteLocationAsPathGeneric | — | Route Location the link should navigate to when clicked on. |
activeClass | string | — | Class to apply when the link is active |
exactActiveClass | string | — | Class to apply when the link is exact active |
inactiveClass | string | — | The class to apply to the link when it is inactive. |
prefetchedClass | string | — | A class to apply to links that have been prefetched. |
external | boolean | — | Forces the link to be considered as external (true) or internal (false). This is helpful to handle edge-cases |
ariaCurrentValue | 'true' | 'false' | 'page' | 'date' | 'time' | 'step' | 'location' | `'page'` | Value passed to the attribute `aria-current` when the link is exact active. |
viewTransition | boolean | — | Pass the returned promise of `router.push()` to `document.startViewTransition()` if supported. |
target | (string & {}) | '_blank' | '_parent' | '_self' | '_top' | null | — | Where to display the linked URL, as the name for a browsing context. |
rel | (string & {}) | 'noopener' | 'noreferrer' | 'nofollow' | 'sponsored' | 'ugc' | null | 'noopener noreferrer' | A rel attribute value to apply on the link. Defaults to "noopener noreferrer" for external links. |
noRel | boolean | — | If set to true, no rel attribute will be added to the link |
prefetch | boolean | — | When enabled will prefetch middleware, layouts and payloads of links in the viewport. |
prefetchOn | 'visibility' | 'interaction' | Partial<{ visibility: boolean; interaction: boolean; }> | — | Allows controlling when to prefetch links. By default, prefetch is triggered only on visibility. |
noPrefetch | boolean | — | Escape hatch to disable `prefetch` attribute. |
trailingSlash | 'append' | 'remove' | — | An option to either add or remove trailing slashes in the `href` for this specific link.
Overrides the global `trailingSlash` option if provided. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
select | CustomEvent<{ originalEvent: Event; }> | Emitted when a link is selected. |
NavMenuList
NavMenuOptionCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
item | NavMenuOptionData | — | Current item data. |
itemProps | NavMenuItemProps | — | Properties forwarded to the item element. |
linkProps | Omit<NavMenuLinkProps, LinkBasePropsKey> | — | Properties forwarded to the link element. |
triggerProps | NavMenuTriggerProps | — | Properties forwarded to the trigger element. |
contentProps | NavMenuContentProps | — | Properties forwarded to the content element. |
viewportProps | NavMenuViewportProps | — | Properties forwarded to the viewport element. |
listProps | NavMenuListProps | — | Properties forwarded to the list element. |
subTriggerProps | NavMenuSubTriggerProps | — | Properties forwarded to the nested flyout trigger element. |
subContentProps | NavMenuSubContentProps | — | Properties forwarded to the nested flyout content element. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
select | CustomEvent<{ originalEvent: Event; }> | Emitted when a link is selected. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
item | props: { item: NavMenuOptionData; isTrigger?: boolean; } | — |
item-leading | props: { item: NavMenuOptionData; } | — |
item-trailing | props: { item: NavMenuOptionData; } | — |
item-link-icon | props: { item: NavMenuOptionData; } | — |
item-trigger-icon | props: { item: NavMenuOptionData; } | — |
item-children | props: { item: NavMenuOptionData; } | — |
NavMenuRoot
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | — | The controlled value of the active item. Can be used as `v-model`. |
defaultValue | string | — | The value of the item that should be active when initially rendered. |
dir | Direction | — | The reading direction of the menu. |
orientation | DataOrientation | — | The orientation of the menu. |
delayDuration | number | 200 | The duration from when the pointer enters the trigger until the submenu opens. |
skipDelayDuration | number | 300 | How much time a user has to enter another trigger without incurring a delay again. |
disableClickTrigger | boolean | false | If `true`, the menu cannot be opened by clicking a trigger. |
disableHoverTrigger | boolean | false | If `true`, the menu cannot be opened by hovering a trigger. |
disablePointerLeaveClose | boolean | false | If `true`, the menu will not close when the pointer leaves the content. |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:modelValue | string | Emitted when the model value changes. |
NavMenuSubContent
Props
| Prop | Type | Default | Description |
|---|---|---|---|
sideOffset | number | — | Distance in pixels between the sub trigger and the nested flyout. |
forceMount | boolean | — | Used to force mounting when more control is needed.
Useful when controlling animation with Vue animation libraries. |
NavMenuSubTrigger
Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | — | Whether the trigger is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
NavMenuTrigger
Props
| Prop | Type | Default | Description |
|---|---|---|---|
disabled | boolean | — | Whether the trigger is disabled. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
NavMenuViewport
Props
| Prop | Type | Default | Description |
|---|---|---|---|
align | Align | — | Align the viewport against the active trigger. |
sideOffset | number | — | Distance in pixels between the trigger and the viewport. |
forceMount | boolean | — | Used to force mounting when more control is needed.
Useful when controlling animation with Vue animation libraries. |
Notes
Architecture
| Capability | NavMenu (Popper-native) |
|---|---|
| floating model | single PopperRoot + viewport as PopperPositioner |
| positioning | Floating UI against the active trigger (dynamic ref) |
| hover delay | shared Popper hover machine (delayDuration / skipDelayDuration) |
| grace corridor | positioner’s useGraceArea anchored on the nav |
| dismissal | positioner’s usePopperDismiss (Escape / outside) |
| value routing | pendingValue slot on the shared root context |
Runtime notes
- Hover timing — the menu opens after
delayDurationon hover; while open (or withinskipDelayDurationof the last close) hovering another trigger switches instantly.disablePointerLeaveClosekeeps the menu open when the pointer leaves the content. - Click vs. hover — a submenu opened by hover ignores the subsequent click (
hasPointerMoveOpenedRefguard), preventing accidental toggling. SetdisableHoverTrigger: truefor a click-only experience. - Controlled / uncontrolled — with
modelValue, internal writes only emitupdate:modelValue; highlight fully follows the prop. - Focus handling — the content participates in Tab navigation via a focus proxy;
Escapereturns focus to the active trigger.
FAQ
How does NavMenu differ from the retired NavigationMenu family?
NavMenu drops the shared measured-size viewport and per-item hover roots in favor of Popper’s native single-root + positioner model: Floating UI positions the viewport, and the grace corridor / dismissal come from the positioner. It trades the old size/motion-direction transitions for a standard floating popup. NavigationMenu and SNavigationMenu were removed in v0.50.0 — see the migration guide.
How do I keep only click triggers?
Set disableHoverTrigger: true — hovering no longer opens submenus while clicks still toggle. For hover-only, set disableClickTrigger: true.
Can a parent item be both a link and have a submenu?
Yes — give the item both href / to and children. The trigger then renders as a link; clicking an open link trigger dismisses the menu first.