Naive UI Menu Component
Menu component for navigation and actions. No Food.
When to Use
Use this component when:
- Side navigation: Create sidebar navigation for applications
- Top navigation: Build horizontal navigation bars
- Nested navigation: Display hierarchical menu structures
- Admin panels: Create collapsible sidebar menus
When to Invoke
Invoke this skill when:
- User needs to implement navigation menus
- User wants to create sidebar or top navigation
- User needs collapsible menu functionality
- User wants to integrate with vue-router
- User asks about menu customization or rendering
Features
- Horizontal & Vertical: Support both layout modes
- Collapsible: Collapse menu with icon-only mode
- Nested Items: Support multi-level menu structure
- Router Integration: Works with vue-router
- Custom Rendering: Custom render functions for labels and icons
- Accordion Mode: Expand one submenu at a time
- Responsive: Auto-collapse overflow items in horizontal mode
API Reference
Menu Props
| Name | Type | Default | Description | Version |
|---|---|---|---|---|
| accordion | boolean |
false |
Whether to use accordion mode. | |
| children-field | string |
'children' |
Field name of children. | |
| collapsed-icon-size | number |
24 |
The icon size when menu is collapsed. | |
| collapsed-width | number |
48 |
The menu width after collapsed. | |
| collapsed | boolean |
false |
The collapsed status of menu. | |
| default-expand-all | boolean |
false |
Whether to expand all menus. | |
| default-expanded-keys | Array<string> |
[] |
The default expanded submenu keys. | |
| default-value | string | null |
null |
Default selected value. | |
| disabled-field | string |
'disabled' |
Field name of disabled. | 2.33.0 |
| dropdown-placement | string |
'top' |
Dropdown placement in horizontal mode. | |
| dropdown-props | DropdownProps |
undefined |
Props for dropdown menus. | |
| expanded-keys | Array<string> |
undefined |
Expanded submenu keys (controlled). | |
| expand-icon | (option) => VNodeChild |
undefined |
Render function for expand icon. | |
| icon-size | number |
20 |
Icon size when not collapsed. | |
| indent | number |
32 |
Menu indent. | |
| inverted | boolean |
false |
Use inverted style. | |
| key-field | string |
'key' |
Field name of key. | |
| label-field | string |
'label' |
Field name of label. | |
| options | Array<MenuOption> |
[] |
Menu items data. | |
| node-props | (option) => object |
undefined |
Node's DOM attrs generator. | 2.28.3 |
| mode | 'vertical' | 'horizontal' |
'vertical' |
Menu layout. | |
| render-extra | (option) => VNodeChild |
undefined |
Render function for extras. | |
| render-icon | (option) => VNodeChild |
undefined |
Render function for icons. | |
| render-label | (option) => VNodeChild |
undefined |
Render function for labels. | |
| responsive | boolean |
false |
Auto-collapse overflow in horizontal mode. | 2.36.0 |
| root-indent | number |
undefined |
Indent for first level children. | |
| value | string | null |
undefined |
Selected item key (controlled). | |
| on-update:expanded-keys | (keys) => void |
undefined |
Callback when expanded keys change. | |
| on-update:value | (key, item) => void |
undefined |
Callback when selection changes. |
MenuOption Properties
| Name | Type | Description |
|---|---|---|
| children? | Array<MenuOption> |
Child menu options. |
| disabled? | boolean |
Whether to disable the item. |
| extra? | string | (() => VNodeChild) |
Extra content. |
| icon? | () => VNodeChild |
Icon render function. |
| key | string |
Unique identifier. |
| label | string | (() => VNodeChild) |
Label content. |
| show? | boolean |
Whether to show the item. |
Menu Methods
| Name | Type | Description | Version |
|---|---|---|---|
| deriveResponsiveState | () => void |
Recalculate responsive state. | 2.36.0 |
| showOption | (key) => void |
Expand menu to show option. | 2.27.0 |
Basic Usage
Horizontal Menu
<template>
<n-menu
v-model:value="activeKey"
mode="horizontal"
:options="menuOptions"
responsive
/>
</template>
<script setup>
import { ref } from 'vue'
import { h } from 'vue'
const activeKey = ref(null)
const menuOptions = [
{
label: 'Home',
key: 'home'
},
{
label: 'About',
key: 'about'
},
{
label: 'Contact',
key: 'contact'
}
]
</script>
Vertical Menu with Selection
<template>
<n-menu :options="menuOptions" @update:value="handleUpdateValue" />
</template>
<script setup>
import { h } from 'vue'
const handleUpdateValue = (key, item) => {
console.log('Selected:', key, item)
}
const menuOptions = [
{
label: 'Dashboard',
key: 'dashboard'
},
{
label: 'Settings',
key: 'settings',
children: [
{ label: 'Profile', key: 'profile' },
{ label: 'Security', key: 'security' }
]
}
]
</script>
Common Patterns
Collapsible Sidebar Menu
<template>
<n-space vertical>
<n-switch v-model:value="collapsed" />
<n-layout has-sider>
<n-layout-sider
bordered
collapse-mode="width"
:collapsed-width="64"
:width="240"
:collapsed="collapsed"
show-trigger
@collapse="collapsed = true"
@expand="collapsed = false"
>
<n-menu
:collapsed="collapsed"
:collapsed-width="64"
:collapsed-icon-size="22"
:options="menuOptions"
/>
</n-layout-sider>
<n-layout>
<span>Content</span>
</n-layout>
</n-layout>
</n-space>
</template>
<script setup>
import { ref } from 'vue'
const collapsed = ref(false)
const menuOptions = [...]
</script>
Menu with Icons
<template>
<n-menu
:options="menuOptions"
:render-icon="renderMenuIcon"
/>
</template>
<script setup>
import { h } from 'vue'
import { HomeOutline, SettingsOutline } from '@vicons/ionicons5'
const renderMenuIcon = (option) => {
if (option.key === 'home') return h(HomeOutline)
if (option.key === 'settings') return h(SettingsOutline)
return null
}
const menuOptions = [
{ label: 'Home', key: 'home' },
{ label: 'Settings', key: 'settings' }
]
</script>
Menu with Vue Router
<template>
<n-menu :options="menuOptions" />
</template>
<script setup>
import { h } from 'vue'
import { RouterLink } from 'vue-router'
const menuOptions = [
{
label: () => h(RouterLink, { to: '/' }, { default: () => 'Home' }),
key: 'home'
},
{
label: () => h(RouterLink, { to: '/about' }, { default: () => 'About' }),
key: 'about'
}
]
</script>
Accordion Mode
<template>
<n-menu
:options="menuOptions"
:default-expanded-keys="defaultExpandedKeys"
accordion
/>
</template>
Inverted Style
<template>
<n-layout has-sider>
<n-layout-sider bordered inverted>
<n-menu inverted :options="menuOptions" />
</n-layout-sider>
<n-layout>
Content
</n-layout>
</n-layout>
</template>
Control Option Visibility
<template>
<n-menu :options="filteredOptions" />
</template>
<script setup>
import { computed } from 'vue'
const isAdmin = ref(false)
const menuOptions = [
{ label: 'Dashboard', key: 'dashboard', show: true },
{ label: 'Admin', key: 'admin', show: isAdmin.value },
{ label: 'Settings', key: 'settings', show: true }
]
const filteredOptions = computed(() =>
menuOptions.filter(item => item.show !== false)
)
</script>
Best Practices
Use with Layout: Combine with
n-layout-siderfor sidebar navigationRouter integration: Render labels as RouterLink for SPA navigation
Collapsed state: Set appropriate
collapsed-widthfor icon-only modeCustom field names: Use
key-field,label-fieldfor backend data mappingAccordion for limited space: Use accordion mode to prevent menu from getting too tall
Responsive horizontal menu: Enable
responsivefor horizontal menus that may overflow