Sidebar

Composable navigation sidebar for app shells with collapsible sections, search and filter, tree views with hover actions, and a mobile menu

import {
SidebarProvider,
SidebarLayout,
Sidebar,
SidebarHeader,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuItem,
SidebarMenuButton,
SidebarInset,
SidebarTrigger,
} from "@plexui/vue/components/Sidebar";

Examples

The examples read their navigation data (navGroups, docsSections, iconSections, consoleSections, ...) from the setup scope, where a real app defines it in <script setup> with icon components imported from @plexui/vue/components/Icon. The default slot of SidebarProvider receives the sidebar state and controls (v-slot="sidebar"), the same object useSidebar() returns.

App Shell

Basic

collapsible
labelSize

Collapsible Sections

Search and Filter

expand

Tree with Hover Actions

action

Mobile Menu

Search Input

API

Structure

A Sidebar is composed of the following parts:

  • SidebarProvider: Handles collapsible state and provides context to all child components.
  • SidebarLayout: Wrapper for sidebar and main content.
  • Sidebar: The sidebar container.
  • SidebarHeader / SidebarFooter: Sticky at the top and bottom of the sidebar.
  • SidebarContent: Scrollable content area.
  • SidebarGroup: Section within the content, with optional label.
  • SidebarMenu / SidebarMenuItem / SidebarMenuButton: Navigation menu items.
  • SidebarMenuSub / SidebarMenuSubButton: Nested submenu items.
  • SidebarInset: Main content area next to the sidebar.
  • SidebarTrigger: Toggle button for collapsing the sidebar.

Use Cmd+B (Mac) or Ctrl+B (Windows/Linux) to toggle the sidebar with keyboard.

SidebarProvider

Handles sidebar state and provides context to all child components. Wrap your application layout in this provider.

PropTypeDefaultDescription
collapsible"offcanvas" | "icon" | "none""offcanvas"Collapse behavior: offcanvas (drawer on mobile, full collapse on desktop), icon (icon rail), or none
default-openbooleantrueInitial open state (uncontrolled)
openboolean—Controlled open state. Bind with v-model:open
@update:open(open: boolean) => void—Emitted when the open state changes; powers v-model:open. Without open the provider keeps its own state and still emits
classstring—Class of the provider's wrapper
styleStyleValue—Style of the provider's wrapper, e.g. --sidebar-width
default slot{ state, open, setOpen, openMobile, setOpenMobile, isMobile, collapsible, toggleSidebar }—Receives the sidebar context, the object useSidebar() returns

The main sidebar container. Renders as an <aside> element.

PropTypeDefaultDescription
variant"sidebar" | "docs""sidebar"Visual variant
side"left" | "right""left"Which side the sidebar is on

SidebarGroup

A section within SidebarContent.

PropTypeDefaultDescription
default-openbooleantrueWhether the group is open initially. A closed group hides its SidebarGroupContent
openboolean—Controlled open state, also exposed as data-state. The group has no toggle of its own: change open from your own control

SidebarGroupLabel

Renders a section label within a SidebarGroup. Supports two size variants.

PropTypeDefaultDescription
size"sm" | "lg""lg"sm: compact, tertiary. lg: larger, primary, more spacing
as-childbooleanfalseRender the single child element as the label

SidebarMenuItem

PropTypeDefaultDescription
expandedboolean—Marks the item expanded, which turns its SidebarMenuChevron. Pair it with open on its SidebarMenuSub

SidebarMenuButton

Renders a menu item button. Use is-active to highlight the current page.

PropTypeDefaultDescription
is-activebooleanfalseHighlight the current page (aria-current="page")
tooltipstring—Label shown on hover when collapsed (icon mode). Hidden on mobile. The tooltip slot takes rich content
as-childbooleanfalseRender the single child element (e.g. a RouterLink) as the button. The button attributes go on the child and its content is wrapped like the button's

SidebarMenuSub

Renders a collapsible submenu container for nested navigation.

PropTypeDefaultDescription
openbooleantrueWhether submenu is expanded. A closed submenu is inert, so its links and buttons leave the tab order and the accessibility tree
has-iconsbooleanfalseAlign child items with parent text that has icons

SidebarMenuSubButton

PropTypeDefaultDescription
is-activebooleanfalseHighlight the current page (aria-current="page")
indent0 | 1 | 2 | 30Nesting level; each level adds 12px of left margin
as-childbooleanfalseRender the single child element (e.g. a RouterLink) as the button

SidebarTrigger

Collapse button. Renders nothing when collapsible="none". The default slot replaces the default icon. A @click listener runs before the toggle; call event.preventDefault() in it to cancel the toggle. SidebarMobileMenuButton, the animated hamburger for the mobile menu, handles @click the same way.

SidebarInput

A search field: Input with a search icon as its start adornment. Every attribute, listener and slot goes to the Input (v-model:value, @clear, ...).

SidebarCard

Renders a promotional card in the sidebar footer for CTAs, upgrades, or announcements.

PropTypeDefaultDescription
variant"default" | "elevated""default"Visual variant
dismissiblebooleanfalseShows dismiss button
@dismiss(event: MouseEvent) => void—Emitted when the dismiss button is clicked

useSidebar

Composable to access sidebar state and controls from the setup() of any component within SidebarProvider. It returns a reactive object: read its fields (sidebar.state) rather than destructuring them outside a render.

  • state: Current state: "expanded" or "collapsed"
  • open / setOpen: Desktop sidebar open state
  • openMobile / setOpenMobile: Mobile menu open state
  • isMobile: Whether the viewport is mobile
  • collapsible: Current collapsible mode
  • toggleSidebar: Toggle function for both desktop and mobile

Styling

Sidebar reads these component tokens. Override them at any scope to restyle it. SidebarProvider sets the three width tokens on its own element, so change those through its style (or on an element inside it):

  • --sidebar-accent: accent (default light-dark(var(--alpha-08), var(--alpha-12)))
  • --sidebar-background: background (default none)
  • --sidebar-border: border (default var(--color-border))
  • --sidebar-collapse-duration: collapse duration (default 0.3s)
  • --sidebar-footer-shadow: footer shadow
  • --sidebar-foreground: foreground (default var(--color-text))
  • --sidebar-group-label-color: group label color (default var(--color-text-tertiary))
  • --sidebar-gutter: gutter (default 12px)
  • --sidebar-gutter-sm: gutter sm (default var(--control-gutter-2xs))
  • --sidebar-header-height: header height (default var(--control-size-3xl))
  • --sidebar-icon-margin: icon margin (default 0 2px 0 4px)
  • --sidebar-icon-size: icon size (default 20px)
  • --sidebar-icon-wrapper-size: icon wrapper size (default 32px)
  • --sidebar-inset-shadow: inset shadow (default none)
  • --sidebar-menu-item-font-size: menu item font size (default 14px)
  • --sidebar-menu-item-height: menu item height (default 38px)
  • --sidebar-menu-item-height-desktop: menu item height desktop (default 32px)
  • --sidebar-menu-item-line-height: menu item line height (default 20px)
  • --sidebar-menu-item-margin: menu item margin (default 0 0 2px)
  • --sidebar-menu-item-margin-desktop: menu item margin desktop (default 0 0 2px)
  • --sidebar-menu-item-padding: menu item padding (default 0)
  • --sidebar-menu-item-radius: menu item radius (default 8px)
  • --sidebar-menu-text: menu text (default light-dark(var(--gray-750), var(--gray-850)))
  • --sidebar-menu-text-active: menu text active (default var(--gray-900))
  • --sidebar-mobile-backdrop-color: mobile backdrop color (default light-dark(rgb(0 0 0 / 15%), rgb(0 0 0 / 35%)))
  • --sidebar-mobile-button-radius: mobile button radius (default 8px)
  • --sidebar-mobile-button-size: mobile button size (default 32px)
  • --sidebar-mobile-enter-delay: mobile enter delay (default 0.33s)
  • --sidebar-mobile-enter-opacity-duration: mobile enter opacity duration (default 0.8s)
  • --sidebar-mobile-enter-transform-duration: mobile enter transform duration (default 0.7s)
  • --sidebar-mobile-exit-duration: mobile exit duration (default 0.3s)
  • --sidebar-mobile-gutter: mobile gutter (default 12px)
  • --sidebar-mobile-header-height: mobile header height (default 48px)
  • --sidebar-mobile-icon-size: mobile icon size (default 18px)
  • --sidebar-mobile-line-color: mobile line color (default var(--gray-900))
  • --sidebar-mobile-line-height: mobile line height (default 2px)
  • --sidebar-mobile-line-transition-duration: mobile line transition duration (default 0.2s)
  • --sidebar-mobile-z-index: mobile z index (default 301)
  • --sidebar-transition-duration: transition duration (default 0.3s)
  • --sidebar-width: width (SidebarProvider sets 240px)
  • --sidebar-width-icon: width icon (SidebarProvider sets 64px)
  • --sidebar-width-mobile: width mobile (SidebarProvider sets 288px)
  • --skeleton-background: background (default light-dark(var(--alpha-08), var(--alpha-12)))
  • --skeleton-radius: radius (default var(--radius-md))