Skip to main content
The LSidebar component provides a full-featured sidebar navigation with collapsible and resizable functionality, logo display, hierarchical navigation links, and customizable footer.

Props

logoDefaultSrc
string
default:"/media/img/ui-logo-placeholder.svg"
Logo image source URL when the sidebar is expanded
logoMiniSrc
string
default:""
Logo image source URL when the sidebar is collapsed. If not provided, uses logoDefaultSrc.
Navigation path when clicking on the logo
Navigation links displayed in the main body of the sidebar. Grouped in arrays.
Navigation links displayed at the bottom of the body section (before footer)
Navigation links displayed in the footer section
defaultSize
number
default:"15"
Default width of the sidebar in the specified unit
minSize
number
default:"10"
Minimum width the sidebar can be resized to
maxSize
number
default:"20"
Maximum width the sidebar can be resized to

Model

v-model:isOpen
boolean
default:"false"
Controls whether the sidebar is open (on mobile) or collapsed state

Slots

header
Custom header content. Receives { collapsed } as slot props. Replaces default logo.
body
Custom body content. Receives { collapsed } as slot props. Replaces default navigation.
Custom footer content. Receives { collapsed } as slot props. Replaces default footer links.

Usage

Basic Sidebar

<template>
  <LSidebar :links-body="navigationLinks" />
</template>

<script setup>
const navigationLinks = [
  [
    { label: 'Dashboard', icon: 'i-lucide-layout-dashboard', to: '/dashboard' },
    { label: 'Projects', icon: 'i-lucide-folder', to: '/projects' },
    { label: 'Tasks', icon: 'i-lucide-check-square', to: '/tasks' }
  ]
]
</script>

Complete Sidebar with All Sections

<template>
  <LSidebar
    logo-default-src="/logo-full.svg"
    logo-mini-src="/logo-icon.svg"
    logo-link="/home"
    :links-body="mainLinks"
    :links-body-bottom="utilityLinks"
    :links-footer="footerLinks"
    v-model:is-open="sidebarOpen"
  />
</template>

<script setup>
const sidebarOpen = ref(true)

const mainLinks = [
  [
    { label: 'Dashboard', icon: 'i-lucide-home', to: '/' },
    { label: 'Analytics', icon: 'i-lucide-bar-chart', to: '/analytics' }
  ],
  [
    { label: 'Projects', icon: 'i-lucide-folder', to: '/projects' },
    { label: 'Team', icon: 'i-lucide-users', to: '/team' }
  ]
]

const utilityLinks = [
  [
    { label: 'Settings', icon: 'i-lucide-settings', to: '/settings' }
  ]
]

const footerLinks = [
  [
    { label: 'Help Center', icon: 'i-lucide-help-circle', to: '/help' },
    { label: 'Feedback', icon: 'i-lucide-message-square', to: '/feedback' }
  ]
]
</script>

Nested Navigation

<template>
  <LSidebar :links-body="nestedLinks" />
</template>

<script setup>
const nestedLinks = [
  [
    {
      label: 'Products',
      icon: 'i-lucide-package',
      children: [
        { label: 'All Products', to: '/products' },
        { label: 'Categories', to: '/products/categories' },
        { label: 'Inventory', to: '/products/inventory' }
      ]
    },
    {
      label: 'Orders',
      icon: 'i-lucide-shopping-cart',
      children: [
        { label: 'All Orders', to: '/orders' },
        { label: 'Pending', to: '/orders/pending' },
        { label: 'Completed', to: '/orders/completed' }
      ]
    }
  ]
]
</script>

Custom Header

<template>
  <LSidebar :links-body="links">
    <template #header="{ collapsed }">
      <div class="flex flex-col items-center p-4">
        <img :src="collapsed ? '/icon.svg' : '/logo.svg'" />
        <span v-if="!collapsed" class="text-sm mt-2">v2.0.0</span>
      </div>
    </template>
  </LSidebar>
</template>

Custom Body with Dynamic Content

<template>
  <LSidebar>
    <template #body="{ collapsed }">
      <div class="space-y-4 p-4">
        <div v-if="!collapsed" class="text-sm text-muted mb-2">
          Recent Projects
        </div>
        <UNavigationMenu
          :collapsed="collapsed"
          :items="recentProjects"
          orientation="vertical"
        />
      </div>
    </template>
  </LSidebar>
</template>

<script setup>
const recentProjects = computed(() => [
  [
    { label: 'Project Alpha', to: '/projects/alpha', icon: 'i-lucide-folder' },
    { label: 'Project Beta', to: '/projects/beta', icon: 'i-lucide-folder' }
  ]
])
</script>
<template>
  <LSidebar :links-body="links">
    <template #footer="{ collapsed }">
      <div class="p-4 border-t">
        <div v-if="!collapsed" class="flex items-center gap-3">
          <UAvatar src="/user.jpg" size="sm" />
          <div>
            <p class="font-medium">John Doe</p>
            <p class="text-xs text-muted">Premium User</p>
          </div>
        </div>
        <UAvatar v-else src="/user.jpg" size="sm" />
      </div>
    </template>
  </LSidebar>
</template>

Controlled Collapsed State

<template>
  <div>
    <LSidebar 
      v-model:is-open="sidebarOpen"
      :links-body="links"
    />
    
    <button @click="sidebarOpen = !sidebarOpen">
      Toggle Sidebar
    </button>
  </div>
</template>

<script setup>
const sidebarOpen = ref(true)
</script>

Custom Size Configuration

<template>
  <LSidebar
    :links-body="links"
    :default-size="18"
    :min-size="12"
    :max-size="25"
  />
</template>

Multi-section Navigation

<template>
  <LSidebar
    :links-body="primaryLinks"
    :links-body-bottom="secondaryLinks"
    :links-footer="footerLinks"
  />
</template>

<script setup>
const primaryLinks = [
  [
    { label: 'Overview', icon: 'i-lucide-layout-dashboard', to: '/' }
  ],
  [
    { label: 'Content', icon: 'i-lucide-file-text', to: '/content' },
    { label: 'Media', icon: 'i-lucide-image', to: '/media' },
    { label: 'Users', icon: 'i-lucide-users', to: '/users' }
  ]
]

const secondaryLinks = [
  [
    { label: 'Settings', icon: 'i-lucide-settings', to: '/settings' },
    { label: 'Integrations', icon: 'i-lucide-plug', to: '/integrations' }
  ]
]

const footerLinks = [
  [
    { label: 'Support', icon: 'i-lucide-life-buoy', to: '/support' },
    { label: 'Documentation', icon: 'i-lucide-book', to: '/docs' }
  ]
]
</script>

Features

Collapsible

  • Click the collapse button to toggle between expanded and collapsed states
  • Collapsed state shows only icons with tooltips
  • Logo switches between full and mini versions

Resizable

  • Drag the sidebar edge to resize
  • Constrained by minSize and maxSize props
  • Size persists across sessions

Tooltips

  • Automatically shows tooltips for navigation items when collapsed
  • Helps users identify menu items by icon alone

Popover Navigation

  • Nested menu items open in popovers when sidebar is collapsed
  • Maintains full navigation capabilities in compact mode

Layout Structure

UDashboardSidebar
├── Header (height: 120px)
│   └── Logo (clickable, centered)
├── Body (scrollable)
│   ├── Collapse Button (positioned absolute)
│   ├── Primary Navigation (linksBody)
│   └── Bottom Navigation (linksBodyBottom)
└── Footer (with border-t)
    └── Footer Links (linksFooter)

Styling

Default UI configuration:
:ui="{ 
  header: 'h-[120px]', 
  footer: 'lg:border-t lg:border-default' 
}"
  • Background: bg-elevated/25 with transition
  • Collapse Button: Positioned at top-right with primary solid color
  • Navigation: Vertical orientation with space-y-2 gap
UNavigationMenu(
  :collapsed="collapsed"
  :items="linksBody"
  orientation="vertical"
  tooltip
  popover
  :ui="{
    list: 'space-y-2',
    childList: 'space-y-2'
  }"
)
Source: /home/daytona/workspace/source/app/components/l/l-sidebar.vue:78

Build docs developers (and LLMs) love