NavSidebar
A vertical navigation component used to display page links in a sidebar layout. Optimized for documentation and dashboard structures.
Component
Props
| Props | Required | Default | Type |
|---|---|---|---|
sidebarId | true | — | string |
menuItems | — | An example array | SidebarMenuItem[] |
expandedWidth | — | 240 | number |
collapsedWidth | — | 60 | number |
multipleSubmenusOpen | — | false | boolean |
isCollapsed | — | false | boolean |
showCollapseDivider | — | false | boolean |
dividerClass | — | border-border-neutral-subtle | string |
collapsedSubmenuOffset | — | 20 | number |
collapsedSubmenuWidth | — | 200 | number |
collapsedSubmenuTrigger | — | Trigger.CLICK | Trigger |
collapsedFlipLimit | — | 8 | number |
showCollapseToggle | — | false | boolean |
collapseTogglePosition | — | Position.BOTTOM | Position |
collapsedStateIcon | — | mdi:menu-close | string |
expandedStateIcon | — | mdi:menu-open | string |
showMobileSidebarClose | — | false | boolean |
mobileSidebarCloseIcon | — | mdi:close | string |
mobileBreakpoint | — | 1024 | number |
isFixed | — | true | boolean |
stickOnScroll | — | false | boolean |
stickyScrollHeight | — | 0 | number |
headerHeight | — | 0 | number |
footerHeight | — | 0 | number |
footerSafeAreaHeight | — | 180 | number |
itemsStyleType | — | — | SidebarNavMenuItemStyleType |
itemsCustomClass | — | — | string |
itemsTextClass | — | — | string |
itemsIconClass | — | — | string |
subItemsCustomClass | — | — | string |
subItemsTextClass | — | — | string |
subItemsIconClass | — | — | string |
thirdLevelItemsCustomClass | — | — | string |
showNestedSectionLevelGuide | — | true | boolean |
prefetchOn | — | PrefetchOn.VISIBILITY | PrefetchOnStrategy |
truncateDepth | — | [] | SidebarMenuDepth[] |
marquee | — | false | boolean |
moreActionsPosition | — | Position.BOTTOM | Position |
moreActionsPositionYOffset | — | 4 | number | string |
closeSidebarAriaLabel | — | 'Close sidebar' | string |
collapseSidebarAriaLabel | — | 'Collapse sidebar' | string |
expandSidebarAriaLabel | — | 'Expand sidebar' | string |
Slots
| Name | Description |
|---|---|
sidebar-header | Template to render a header for the sidebar. It is commonly used for logos, language selectors, etc. |
sidebar-menu | Template to fully replace the default menu. When used, it renders instead of the built-in menu built from the `menuItems` prop. |
sidebar-menu-prefix-content | Template to render the prefix content before the menu items. It will render the content before the menu items inside the menu. |
sidebar-menu-suffix-content | Template to render the suffix content before the menu items. It will render the content after the menu items inside the menu. |
sidebar-footer | Template to render a footer for the sidebar. It is commonly used for footer menus, banners, etc. |
<template>
<NavSidebar
sidebarId="example-nav-sidebar"
:menuItems="routeItems"
>
<template #sidebar-header>
<!-- Add content here -->
</template>
<template #sidebar-menu>
<!-- Add content here -->
</template>
<template #sidebar-menu-prefix-content>
<!-- Add content here -->
</template>
<template #sidebar-menu-suffix-content>
<!-- Add content here -->
</template>
<template #sidebar-footer>
<!-- Add content here -->
</template>
</NavSidebar>
</template>
<script setup lang="ts">
const routeItems: SidebarMenuItem[] = [
// ...
]
</script>
Usage
sidebarId
A unique identifier for the sidebar. It is used to manage the state of the sidebar (collapsed or expanded) when using multiple sidebars in the same application.
<template>
<NavSidebar
sidebarId="main-sidebar"
/>
</template>
menuItems
An array of objects used to render the menu items.
<template>
<NavSidebar
:menuItems="routeItems"
/>
</template>
<script setup lang="ts">
const routeItems: SidebarMenuItem[] = [
{
isSectionTitle: true,
text: 'Section title',
icon: 'mdi:bullseye',
},
{
text: 'Item 1',
icon: 'mdi:help',
to: '/',
},
{
text: 'Item 2',
icon: 'mdi:help',
to: '/',
},
{
isDivider: true,
},
{
text: 'Item 3',
icon: 'mdi:help',
children: [
{
isSectionTitle: true,
text: 'Subsection title',
icon: 'mdi:shape-outline',
},
{
text: 'Subitem 1',
icon: 'mdi:help',
children: [
{
isSectionTitle: true,
text: 'Nested subsection title',
icon: 'mdi:format-list-bulleted-square',
},
{
text: 'Third level item',
icon: 'mdi:help-circle-outline',
to: '/',
},
],
},
{
text: 'Subitem 2',
icon: 'mdi:help',
to: '/',
},
],
},
]
</script>
NavSidebar supports up to 3 nested menu levels. You can render subsection headers at any level by setting isSectionTitle: true.
When the sidebar is collapsed, submenu labels are rendered as plain text labels without prefix markers.
Set isDivider: true on an item to render a horizontal Divider between entries instead of a link. Divider items need no other fields. They work at any nesting level and, when the sidebar is collapsed, become a separator line inside the submenu dropdown.
Set detectActive: false on an item to opt it out of route-based active highlighting. By default every item with a to highlights itself when the current route matches.
TypeScript interface
interface SidebarMenuItem {
text?: string
icon?: string
to?: string
isSectionTitle?: boolean
isDivider?: boolean
children?: SidebarMenuItem[]
disabled?: boolean
detectActive?: boolean
moreActionsItems?: DropdownMenuItem[]
suffix?: () => VNode | VNode[]
textSuffix?: () => VNode | VNode[]
}
Per-item slots
`suffix` (replaces the more-actions trigger) and `textSuffix` (renders right after the label, e.g. for a badge) can be set directly on a `menuItems` entry as render functions. They're also available as `#suffix`/`#text-suffix` template slots when rendering `NavSidebarMenuItem` directly.
| Name | Description |
|---|---|
suffix | Replaces the built-in more-actions trigger entirely. Renders right-aligned on row hover/focus. |
text-suffix | Renders inline right after the item's label, e.g. for a badge or a count. Never clipped by `truncate`/`marquee`, and hidden when the sidebar is collapsed. |
Set suffix/textSuffix on a menuItems entry as a render function:
<template>
<NavSidebar sidebarId="main-sidebar" :menuItems="routeItems" />
</template>
<script setup lang="ts">
import { h } from 'vue'
const routeItems: SidebarMenuItem[] = [
{
text: 'Inbox',
icon: 'mdi:inbox-outline',
to: '/inbox',
textSuffix: () => h(Badge, { text: '12', color: ColorAccent.SUCCESS }),
},
]
</script>
Or pass them as template slots when rendering NavSidebarMenuItem directly, e.g. inside NavSidebar's #sidebar-menu slot:
<template>
<NavSidebar sidebarId="main-sidebar">
<template #sidebar-menu>
<NavSidebarMenu>
<NavSidebarMenuItem text="Inbox" icon="mdi:inbox-outline" to="/inbox">
<template #text-suffix>
<Badge text="12" :color="ColorAccent.SUCCESS" />
</template>
</NavSidebarMenuItem>
</NavSidebarMenu>
</template>
</NavSidebar>
</template>
expandedWidth
A number value that sets the width of the sidebar when it is expanded.
<template>
<NavSidebar
:expandedWidth="300"
/>
</template>
collapsedWidth
A number value that sets the width of the sidebar when it is collapsed.
<template>
<NavSidebar
:collapsedWidth="80"
/>
</template>
multipleSubmenusOpen
A boolean value that determines whether multiple submenus can be open at the same time.
<template>
<NavSidebar
multipleSubmenusOpen
/>
</template>
isCollapsed
A boolean value that determines whether to render the sidebar in a collapsed state.
There are two ways to control the collapsed state of the sidebar:
With v-model
<template>
<NavSidebar
v-model:isCollapsed="isSidebarCollapsed"
/>
</template>
<script setup lang="ts">
const isSidebarCollapsed = ref(false)
</script>
With the useSidebar composable
<template>
<NavSidebar
:isCollapsed="isSidebarCollapsed('example-nav-sidebar')"
/>
</template>
<script setup lang="ts">
const { isSidebarCollapsed } = useSidebar()
</script>
Note
If you use collapsed state, make sure to pass icons for each menu item, otherwise they won't be visible when the sidebar is collapsed. Additionally, the sidebar will require a unique `sidebarId` prop to manage its state.
<template>
<NavSidebar
sidebarId="example-nav-sidebar"
isCollapsed
/>
</template>
showCollapseDivider
A boolean value that determines whether to render the section title as a divider when the sidebar is collapsed.
<template>
<NavSidebar
isCollapsed
showCollapseDivider
/>
</template>
dividerClass
Sets the border class applied to divider items ({ isDivider: true }) in the menu. Use it to change the divider color or thickness.
<template>
<NavSidebar
dividerClass="border-border-default"
/>
</template>
collapsedSubmenuOffset
The collapsedSubmenuOffset prop allows you to set the horizontal offset for submenus when the sidebar is in a collapsed state. This offset determines how far the submenu will be positioned horizontally from the edge of the icon.
<template>
<NavSidebar
isCollapsed
:collapsedSubmenuOffset="30"
/>
</template>
collapsedSubmenuWidth
The collapsedSubmenuWidth prop allows you to set the width for submenus dropdown when the sidebar is in a collapsed state.
<template>
<NavSidebar
isCollapsed
:collapsedSubmenuMinWidth="250"
/>
</template>
collapsedSubmenuTrigger
The collapsedSubmenuTrigger prop allows you to control how submenu dropdowns are opened when the sidebar is collapsed.
Collapsed submenu dropdowns support contextual lateral nesting up to 3 levels and render section titles (isSectionTitle: true) as non-interactive headers.
<template>
<NavSidebar
isCollapsed
:collapsedSubmenuTrigger="Trigger.HOVER"
/>
</template>
Options
| Value | Description |
|---|---|
CLICK | Open the collapsed submenu when clicking the icon item. |
HOVER | Open the collapsed submenu when hovering the icon item. |
collapsedFlipLimit
The submenu dropdown will have two possible opening directions in the collapsed state: upwards or downwards.
The collapsedFlipLimit prop allows you to specify how many of the submenu dropdowns should open downwards when the sidebar is in a collapsed state.
This is particularly useful for ensuring that submenus do not extend beyond the viewport when there are many items in the sidebar.
<template>
<NavSidebar
:collapsedDropdownTopCount="5"
/>
</template>
showCollapseToggle
A boolean value that determines whether to render the collapse toggle button to collapse or expand the sidebar.
<template>
<NavSidebar
showCollapseToggle
/>
</template>
collapseTogglePosition
The collapseTogglePosition prop allows you to set the position of the collapse toggle button. It uses the Position enum.
<template>
<NavSidebar
showCollapseToggle
:collapseTogglePosition="Position.TOP"
/>
</template>
Options
| Value | Description |
|---|---|
TOP | The toggle button is rendered at the top of the sidebar. |
BOTTOM | The toggle button is rendered at the bottom of the sidebar. |
collapsedStateIcon
The collapsedStateIcon prop allows you to set a custom icon for the collapse toggle button when the sidebar is in a collapsed state.
<template>
<NavSidebar
showCollapseToggle
collapsedStateIcon="mdi:arrow-right-bold-circle"
/>
</template>
expandedStateIcon
The expandedStateIcon prop allows you to set a custom icon for the collapse toggle button when the sidebar is in an expanded state.
<template>
<NavSidebar
showCollapseToggle
expandedStateIcon="mdi:arrow-left-bold-circle"
/>
</template>
showMobileSidebarClose
A boolean value that determines whether to render the close button in the for the mobile sidebar.
It will only be visible on small screen widths and when the sidebar do not use the collapsed mode.
<template>
<NavSidebar
showMobileSidebarClose
/>
</template>
mobileSidebarCloseIcon
The mobileSidebarCloseIcon prop allows you to set a custom icon for the close button in the mobile sidebar.
<template>
<NavSidebar
showMobileSidebarClose
mobileSidebarCloseIcon="mdi:close-circle"
/>
</template>
mobileBreakpoint
The mobileBreakpoint prop sets the viewport width (in pixels) below which the sidebar switches to its mobile layout. It is the single source of truth for responsive switching: below it the sidebar behaves as a slide-in drawer (toggled by the mobile sidebar control); at or above it the sidebar is always visible. The value is reactive: changing it updates the layout immediately.
<template>
<NavSidebar
:mobileBreakpoint="1024"
/>
</template>
isFixed
A boolean value that determines whether to render the sidebar as a fixed element.
<template>
<NavSidebar
isFixed
/>
</template>
stickOnScroll
The stickOnScroll prop allows the sidebar to adjust its vertical position dynamically based on the user's scroll position. When enabled, it simulates a "sticky" behavior: the sidebar initially appears slightly offset from the top (defined by stickyScrollHeight), and as the user scrolls past that threshold, the sidebar sticks to the very top.
It requires isFixed to be true.
<template>
<NavSidebar
stickOnScroll
/>
</template>
stickyScrollHeight
The stickyScrollHeight prop determines the height at which the sidebar will stick to the top when stickOnScroll is enabled. This value is used to calculate the initial offset of the sidebar.
It requires isFixed to be true.
<template>
<NavSidebar
stickOnScroll
stickyScrollHeight="85"
/>
</template>
headerHeight
The headerHeight prop specifies the height of the header to the sidebar menu. It is used to calculate the total height of the sidebar menu and is particularly useful in layouts where the sidebar is below a sticky header.
<template>
<NavSidebar
:headerHeight="100"
/>
</template>
footerHeight
The footerHeight prop specifies the height of the footer to the sidebar menu. It is used to calculate the total height of the sidebar menu and is particularly useful in layouts where the sidebar is above a sticky footer.
<template>
<NavSidebar
:footerHeight="40"
/>
</template>
footerSafeAreaHeight
The footerSafeAreaHeight prop specifies a safe height for the sidebar menu in the event of using the bottom footer of the sidebar. It is used to calculate the total height of the sidebar menu.
<template>
<NavSidebar
:footerSafeAreaHeight="180"
/>
</template>
itemsStyleType
The itemsStyleType prop allows you to set the style of the menu items. It uses the SidebarNavMenuItemStyleType enum.
Important: It only works when menu items are passed as an array by using the menuItems props.
<template>
<NavSidebar
:itemsStyleType="SidebarNavMenuItemStyleType.SPACED"
/>
</template>
Options
| Value | Description |
|---|---|
SPACED | The items have more space around them. |
COMPACT | The items padding is more condensed and the optional icon is slightly smaller. |
itemsCustomClass
The itemsCustomClass prop allows you to set custom classes on the menu item wrapper.
Use this prop for wrapper-level styles such as spacing, background, border, or font weight.
<template>
<NavSidebar
:itemsCustomClass="'!rounded-xl !border !border-border-brand'"
/>
</template>
itemsTextClass
The itemsTextClass prop allows you to set custom classes specifically for top-level menu item text.
<template>
<NavSidebar
:itemsTextClass="'!text-text-primary-brand-default'"
/>
</template>
itemsIconClass
The itemsIconClass prop allows you to set custom classes specifically for top-level menu item icon.
<template>
<NavSidebar
:itemsIconClass="'!text-icon-primary-brand-default'"
/>
</template>
subItemsCustomClass
The subItemsCustomClass prop allows you to set custom classes on expanded child menu item wrappers (nested items).
<template>
<NavSidebar
:subItemsCustomClass="'!rounded-lg !font-bold'"
/>
</template>
subItemsTextClass
The subItemsTextClass prop allows you to set custom classes specifically for expanded child menu item text.
<template>
<NavSidebar
:subItemsTextClass="'!text-text-danger'"
/>
</template>
subItemsIconClass
The subItemsIconClass prop allows you to set custom classes specifically for expanded child menu item icon.
<template>
<NavSidebar
:subItemsIconClass="'!text-icon-danger'"
/>
</template>
thirdLevelItemsCustomClass
The thirdLevelItemsCustomClass prop allows you to set custom classes on expanded third-level menu item wrappers.
<template>
<NavSidebar
:thirdLevelItemsCustomClass="'!text-text-primary-brand-default !italic'"
/>
</template>
showNestedSectionLevelGuide
Controls whether nested entries (section titles and submenu items on level 2 and level 3) render a left vertical guide line in expanded mode.
<template>
<NavSidebar
:showNestedSectionLevelGuide="true"
/>
</template>
prefetchOn
Controls when sidebar route targets should be prefetched. It uses either the PrefetchOnStrategy type or the PrefetchOn enum.
<template>
<NavSidebar
sidebarId="main-sidebar"
:menuItems="routeItems"
:prefetchOn="PrefetchOn.INTERACTION"
/>
</template>
Options
| Value | Description |
|---|---|
VISIBILITY | Prefetches routes based on visibility strategy. |
INTERACTION | Prefetches routes when users hover or focus a sidebar item with a route target. |
truncateDepth
An array of SidebarMenuDepth values that determines which nesting levels render their item text as single-line, ellipsis-truncated labels instead of wrapping across multiple lines. Levels don't need to be contiguous: you can truncate level 1 and level 3 while leaving level 2 items free to wrap.
<template>
<NavSidebar
sidebarId="main-sidebar"
:menuItems="routeItems"
:truncateDepth="[SidebarMenuDepth.LEVEL_1, SidebarMenuDepth.LEVEL_2]"
/>
</template>
Options
| Value | Description |
|---|---|
LEVEL_1 | Top-level menu items. |
LEVEL_2 | First-level nested (child) items. |
LEVEL_3 | Second-level nested (grandchild) items. |
marquee
A boolean value that slides an item's full text into view on hover instead of leaving it ellipsis-truncated. It only affects items whose level is already in truncateDepth, so there's no separate per-level control for it.
<template>
<NavSidebar
sidebarId="main-sidebar"
:menuItems="routeItems"
:truncateDepth="[SidebarMenuDepth.LEVEL_1]"
marquee
/>
</template>
moreActionsItems
Set moreActionsItems on a menuItems entry to render a "more actions" trigger button on that item, opening a DropdownMenu with the given items. It's visible on row hover/focus and is automatically suppressed on parent items that already show a dropdown arrow for their children.
<script setup lang="ts">
const routeItems: SidebarMenuItem[] = [
{
text: 'Inbox',
icon: 'mdi:inbox-outline',
to: '/inbox',
moreActionsItems: [
{ text: 'Mark all as read', icon: 'mdi:email-check-outline' },
{ text: 'Archive', icon: 'mdi:archive-outline' },
],
},
]
</script>
Use moreActionsPosition and moreActionsPositionYOffset below to control where this dropdown opens relative to its trigger. The menu is always right-aligned with the trigger; only the vertical side is configurable.
moreActionsPosition
Sets the preferred vertical side of the built-in "more actions" dropdown menu (rendered when an item defines moreActionsItems) relative to its trigger button. Uses the Position enum. Each row measures the space above and below it on hover/focus and automatically flips to the opposite side when the preferred side doesn't have enough room, so the menu never overflows the viewport.
<template>
<NavSidebar
sidebarId="main-sidebar"
:menuItems="routeItems"
:moreActionsPosition="Position.TOP"
/>
</template>
Options
| Value | Description |
|---|---|
TOP | Places the menu above the trigger, right-aligned; flips below it if there isn't enough room above. |
BOTTOM | Places the menu below the trigger, right-aligned; flips above it if there isn't enough room below. |
moreActionsPositionYOffset
Sets the vertical offset of the "more actions" dropdown menu relative to its trigger. Positive values move the menu down, negative values move it up.
<template>
<NavSidebar
sidebarId="main-sidebar"
:menuItems="routeItems"
:moreActionsPositionYOffset="4"
/>
</template>
closeSidebarAriaLabel
The closeSidebarAriaLabel prop sets the accessible label for the mobile close sidebar button. Override it for i18n.
<template>
<NavSidebar
closeSidebarAriaLabel="Cerrar barra lateral"
/>
</template>
collapseSidebarAriaLabel
The collapseSidebarAriaLabel prop sets the accessible label for the collapse sidebar toggle button. Override it for i18n.
<template>
<NavSidebar
collapseSidebarAriaLabel="Contraer barra lateral"
/>
</template>
expandSidebarAriaLabel
The expandSidebarAriaLabel prop sets the accessible label for the expand sidebar toggle button. Override it for i18n.
<template>
<NavSidebar
expandSidebarAriaLabel="Expandir barra lateral"
/>
</template>
You can also pass an object strategy:
{
visibility: true,
interaction: true,
}