Back to catalog
Component detail entry
Navigation
@vean/ui
SNavMenu

NavMenu

NavMenu belongs to the Navigation category. Start with demos and usage, then use the API tables to finish integration and styling decisions.

Use the quick jump panel to reach overview, usage, demos, and API first, then continue through the full page.

Quick jump

Related components

If you are composing interactions in the same family, these components usually pair well together.

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, one PopperPositioner (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 / disableHoverTrigger turn 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 skipDelayDuration and disablePointerLeaveClose tuning closing behavior.
  • Submenu surfaces — items with children open a floating viewport anchored to their trigger, with an arrow indicator and unmountOnHide control.
  • Hidden items — hidden drops 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 / Space activate a link or toggle a submenu; the entry arrow key moves focus into the content; Escape closes and restores focus to the trigger.
  • Popup arrow — the viewport carries a PopperArrow that points at the active trigger; its placement and rotation come from Floating UI’s arrow middleware, and it slides with the viewport on trigger switch.
  • Controlled / uncontrolled — with modelValue, highlight follows the prop and update:modelValue fires; otherwise defaultValue seeds the initial open item.
  • Bidirectional — orientation supports horizontal / vertical; dir supports 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 / Viewport plus the Compact series are exported from @vean/aria/nav-menu.

Usage

Demos

Basic

Nested

API

Aria Layer

12

NavMenu

21 Props2 Emits6 Slots
Props
PropTypeDefaultDescription
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 NameParametersDescription
update:modelValue
string
Emitted when the model value changes.
select
CustomEvent<{ originalEvent: Event; }>
Emitted when a link is selected.
Slots
Slot NameParametersDescription
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

18 Props2 Emits6 Slots
Props
PropTypeDefaultDescription
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 NameParametersDescription
update:modelValue
string
Emitted when the model value changes.
select
CustomEvent<{ originalEvent: Event; }>
Emitted when a link is selected.
Slots
Slot NameParametersDescription
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

1 Props
Props
PropTypeDefaultDescription
forceMount
boolean
—
Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries.

NavMenuItem

1 Props
Props
PropTypeDefaultDescription
value
string
—
Value associated with the current item.

NavMenuLink

22 Props1 Emits
Props
PropTypeDefaultDescription
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 NameParametersDescription
select
CustomEvent<{ originalEvent: Event; }>
Emitted when a link is selected.

NavMenuList

NavMenuOptionCompact

9 Props1 Emits6 Slots
Props
PropTypeDefaultDescription
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 NameParametersDescription
select
CustomEvent<{ originalEvent: Event; }>
Emitted when a link is selected.
Slots
Slot NameParametersDescription
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

9 Props1 Emits
Props
PropTypeDefaultDescription
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 NameParametersDescription
update:modelValue
string
Emitted when the model value changes.

NavMenuSubContent

2 Props
Props
PropTypeDefaultDescription
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

3 Props
Props
PropTypeDefaultDescription
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

3 Props
Props
PropTypeDefaultDescription
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

3 Props
Props
PropTypeDefaultDescription
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

CapabilityNavMenu (Popper-native)
floating modelsingle PopperRoot + viewport as PopperPositioner
positioningFloating UI against the active trigger (dynamic ref)
hover delayshared Popper hover machine (delayDuration / skipDelayDuration)
grace corridorpositioner’s useGraceArea anchored on the nav
dismissalpositioner’s usePopperDismiss (Escape / outside)
value routingpendingValue slot on the shared root context

Runtime notes

  1. Hover timing — the menu opens after delayDuration on hover; while open (or within skipDelayDuration of the last close) hovering another trigger switches instantly. disablePointerLeaveClose keeps the menu open when the pointer leaves the content.
  2. Click vs. hover — a submenu opened by hover ignores the subsequent click (hasPointerMoveOpenedRef guard), preventing accidental toggling. Set disableHoverTrigger: true for a click-only experience.
  3. Controlled / uncontrolled — with modelValue, internal writes only emit update:modelValue; highlight fully follows the prop.
  4. Focus handling — the content participates in Tab navigation via a focus proxy; Escape returns 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.

Changelog

Version history is grouped by release so you can quickly see what changed for this component across published versions.

v0.40.0

2026-09-14

Compare release

Feature

add selected state support for nav menu options

Scope: nav-menuAuthors: soybeanfeView commitb903d

Fix

allow sub link label to wrap

Scope: nav-menuAuthors: soybeanfeView commitad3a7

Feature

add selected state support for nav menu options

Scope: nav-menuAuthors: soybeanfeView commitb903d