Skip to content

Sidebar ​

A vertical sidebar navigation with collapsible submenus, search, drag & drop, resize, groups, badges, collapsed mode, mini mode, and more.

For horizontal navigation, see Menu.

Basic ​

vue
<script setup>
import { ref } from 'vue'
import { VxSidebar } from 'veloxui'

const active = ref('inbox')

const items = [
  { key: 'inbox', label: 'Inbox', icon: 'mail' },
  { key: 'starred', label: 'Starred', icon: 'star' },
  { key: 'drafts', label: 'Drafts', icon: 'file' },
  { key: 'trash', label: 'Trash', icon: 'trash' },
]
</script>

<template>
  <VxSidebar v-model="active" :items="items" />
</template>

With Badges ​

vue
<VxSidebar v-model="active" :items="[
  { key: 'inbox', label: 'Inbox', icon: 'mail', badge: 12, badgeType: 'primary' },
  { key: 'starred', label: 'Starred', icon: 'star', badge: 'New', badgeType: 'success' },
  { key: 'alerts', label: 'Alerts', icon: 'heart', badge: 3, badgeType: 'danger' },
]" />

Badge truncation ^0.1.10 ​

Numeric badges greater than 9 collapse to +9 and reveal the full value in a tooltip on hover. Opt out per item with fullBadge: true.

vue
<VxSidebar v-model="active" :items="[
  { key: 'inbox', label: 'Inbox', icon: 'mail', badge: 3, badgeType: 'primary' },
  { key: 'alerts', label: 'Alerts', icon: 'heart', badge: 42, badgeType: 'danger' },       // "+9" → hover shows 42
  { key: 'updates', label: 'Updates', icon: 'refresh-cw', badge: 128, badgeType: 'warning' }, // "+9" → hover shows 128
  { key: 'spam', label: 'Spam', icon: 'trash', badge: 500, fullBadge: true },                // "500" (no truncation)
]" />

Nested Submenus ​

Click a parent item to expand/collapse its children.

vue
<VxSidebar v-model="active" :items="[
  {
    key: 'projects',
    label: 'Projects',
    icon: 'folder',
    children: [
      { key: 'design', label: 'Design System' },
      { key: 'api', label: 'API Platform' },
    ],
  },
]" />

Grouped ​

Group items under section headers with the group property.

vue
<VxSidebar v-model="active" :items="[
  { key: 'inbox', label: 'Inbox', icon: 'mail', group: 'Communication' },
  { key: 'chat', label: 'Chat', icon: 'heart', group: 'Communication' },
  { key: 'analytics', label: 'Analytics', icon: 'search', group: 'Workspace' },
]" />

Notification Dots ​

Small colored indicators on items. Default red, customizable with dotColor.

vue
<VxSidebar v-model="active" :items="[
  { key: 'inbox', label: 'Inbox', icon: 'mail', dot: true },
  { key: 'chat', label: 'Chat', icon: 'heart', dot: true, dotColor: '#22c55e' },
]" />

Searchable ​

Enable searchable to add a search input that filters items by label.

vue
<VxSidebar v-model="active" :items="items" searchable />
<VxSidebar v-model="active" :items="items" searchable search-placeholder="Find..." />

Collapsed ​

Toggle collapsed to show only icons with tooltips. Submenus appear as popouts.

vue
<script setup>
import { ref } from 'vue'

const active = ref('inbox')
const collapsed = ref(false)

const items = [
  { key: 'home', label: 'Home', icon: 'home' },
  { key: 'inbox', label: 'Inbox', icon: 'mail', badge: 5, badgeType: 'primary' },
  {
    key: 'projects',
    label: 'Projects',
    icon: 'folder',
    children: [
      { key: 'design', label: 'Design System' },
      { key: 'api', label: 'API Platform' },
      { key: 'mobile', label: 'Mobile App' },
    ],
  },
  {
    key: 'settings',
    label: 'Settings',
    icon: 'settings',
    children: [
      { key: 'profile', label: 'Profile', icon: 'user' },
      { key: 'security', label: 'Security', icon: 'link' },
      { key: 'billing', label: 'Billing' },
    ],
  },
  { key: 'logout', label: 'Log Out', icon: 'logout', divider: true },
]
</script>

<template>
  <VxButton @click="collapsed = !collapsed">
    {{ collapsed ? 'Expand' : 'Collapse' }}
  </VxButton>
  <VxSidebar v-model="active" :items="items" :collapsed="collapsed" />
</template>

Mini Mode ​

When collapsed with mini, shows the first letter of each label instead of icons.

vue
<VxSidebar v-model="active" :items="items" collapsed mini />

Resizable ​

Enable resizable to allow dragging the sidebar edge to resize. Drag the right edge of the sidebar.

← drag the edge to resize
vue
<script setup>
import { ref } from 'vue'
import { VxSidebar } from 'veloxui'

const active = ref('inbox')
const collapsed = ref(false)

const items = [
  { key: 'inbox', label: 'Inbox', icon: 'mail' },
  { key: 'starred', label: 'Starred', icon: 'star' },
  { key: 'drafts', label: 'Drafts', icon: 'file' },
  { key: 'trash', label: 'Trash', icon: 'trash' },
]
</script>

<template>
  <VxSidebar
    v-model="active"
    v-model:collapsed="collapsed"
    :items="items"
    resizable
    :min-width="160"
    :max-width="360"
  />
</template>

Draggable ​

Enable draggable to allow reordering top-level items by drag and drop.

vue
<script setup>
import { ref } from 'vue'
import { VxSidebar } from 'veloxui'

const active = ref('inbox')
const items = ref([
  { key: 'inbox', label: 'Inbox', icon: 'mail' },
  { key: 'starred', label: 'Starred', icon: 'star' },
  { key: 'drafts', label: 'Drafts', icon: 'file' },
  { key: 'trash', label: 'Trash', icon: 'trash' },
  { key: 'settings', label: 'Settings', icon: 'settings' },
])

function onReorder(newItems) {
  items.value = newItems
}
</script>

<template>
  <VxSidebar v-model="active" :items="items" draggable @reorder="onReorder" />
</template>

Router Integration ​

Use the to property on items for SPA navigation.

vue
<VxSidebar v-model="active" :items="[
  { key: 'home', label: 'Home', icon: 'home', to: '/' },
  { key: 'about', label: 'About', icon: 'user', to: '/about' },
]" />

Context Menu ​

Right-click on "Inbox" or "Starred" to see the context menu.

vue
<script setup>
import { ref } from 'vue'
import { VxSidebar } from 'veloxui'

const active = ref('inbox')

const items = [
  {
    key: 'inbox',
    label: 'Inbox',
    icon: 'mail',
    badge: 5,
    badgeType: 'primary',
    contextMenu: [
      { label: 'Mark all read', icon: 'check', action: (item) => {} },
      { label: 'Settings', icon: 'settings', action: (item) => {} },
      { label: 'Delete all', icon: 'trash', destructive: true, action: (item) => {} },
    ],
  },
  {
    key: 'starred',
    label: 'Starred',
    icon: 'star',
    contextMenu: [
      { label: 'Clear starred', icon: 'x', action: (item) => {} },
    ],
  },
  { key: 'drafts', label: 'Drafts', icon: 'file' },
  { key: 'settings', label: 'Settings', icon: 'settings' },
]
</script>

<template>
  <VxSidebar v-model="active" :items="items" />
</template>

Loading ​

Show skeleton placeholders while data loads.

vue
<VxSidebar loading :loading-count="6" />

Dark Theme ​

vue
<VxSidebar v-model="active" :items="items" theme="dark" />

Accordion ​

Set accordion to only allow one submenu open at a time. Click "Projects" then "Settings" — only one stays open.

vue
<script setup>
import { ref } from 'vue'
import { VxSidebar } from 'veloxui'

const active = ref('home')

const items = [
  { key: 'home', label: 'Home', icon: 'home' },
  {
    key: 'projects',
    label: 'Projects',
    icon: 'folder',
    children: [
      { key: 'design', label: 'Design System' },
      { key: 'api', label: 'API Platform' },
    ],
  },
  {
    key: 'settings',
    label: 'Settings',
    icon: 'settings',
    children: [
      { key: 'profile', label: 'Profile', icon: 'user' },
      { key: 'security', label: 'Security', icon: 'link' },
    ],
  },
  { key: 'logout', label: 'Log Out', icon: 'logout' },
]
</script>

<template>
  <VxSidebar v-model="active" :items="items" accordion />
</template>

API ​

VxSidebar Props ​

PropTypeDefaultDescription
itemsMenuItem[][]Sidebar items
v-modelstring''Active item key
collapsedbooleanfalseCollapse to icons only
theme'light' | 'dark''light'Color theme
accordionbooleanfalseOnly one submenu open at a time
indentnumber24Child item indent in px
collapsed-widthnumber48Width when collapsed
widthnumber | string240Sidebar width
roundedbooleantrueRounded item corners
searchablebooleanfalseShow search input
search-placeholderstring'Search...'Search input placeholder
draggablebooleanfalseEnable drag & drop reorder
minibooleanfalseShow first letter when collapsed
resizablebooleanfalseEnable resize handle
min-widthnumber160Min width when resizing
max-widthnumber400Max width when resizing
scroll-shadowbooleantrueShow scroll shadows
loadingbooleanfalseShow skeleton placeholders
loading-countnumber6Number of skeleton items
root-classstring | string[] | object—Class on root
PropertyTypeRequiredDescription
keystringYesUnique identifier
labelstringYesDisplay text
iconstring | ComponentNoBuilt-in icon name or Vue component
disabledbooleanNoDisable this item
badgestring | numberNoBadge content. Numeric values > 9 collapse to +9; full value appears in a tooltip on hover
badgeType'default' | 'primary' | 'success' | 'warning' | 'danger'NoBadge color
fullBadgebooleanNoAlways show the full badge value without +9 truncation
groupstringNoGroup header label
dividerbooleanNoShow divider above
action(item) => voidNoClick callback
childrenMenuItem[]NoNested submenu items
tostring | objectNoVue Router navigation target
dotbooleanNoShow notification dot
dotColorstringNoDot color (default red)
pinnedbooleanNoPin item to top
contextMenuContextMenuItem[]NoRight-click menu items

Slots ​

SlotPropsDescription
header—Content above navigation
footer—Content below navigation
item{ item, active }Custom item rendering

Events ​

EventPayloadDescription
update:modelValue(key: string)Active item changed
update:collapsed(collapsed: boolean)Auto-collapse when resized below min-width
select(key, item)Item selected
open-change(openKeys: string[])Submenu opened/closed
search(query: string)Search input changed
reorder(items: MenuItem[])Items reordered via drag
resize(width: number)Sidebar resized
context-menu(item, event)Right-click on item
pin(item)Item pinned/unpinned

Exposed ​

NameTypeDescription
openKeys()() => string[]Get currently open submenu keys
toggleOpen(key)(key: string) => voidToggle a submenu