Skip to content

Sidebar ​

A composable, themeable app sidebar (shadcn-vue Sidebar). It docks beside the content when there is room and turns into a sheet when there is not — measured from its own container, no breakpoints. Collapse to an icon rail or off-canvas with an animated width.

Playground ​

Drag the width slider; try the variants and collapse modes.

Approximate web preview — the HarmonyOS rendering is the reference.

Anatomy ​

SSidebarProvider owns the state (open, openMobile, value) and lays out SSidebar + SSidebarInset. Parts: SSidebarHeader, SSidebarContent (scrolls), SSidebarFooter, SSidebarGroup, SSidebarGroupLabel, SSidebarMenu, SSidebarMenuItem, SSidebarMenuButton (icon, text, badge, isActive or value), SSidebarSeparator, SSidebarTrigger.

Tokens ​

The look is themeable through --sidebar-* component tokens (ADR D13) on top of the semantic --sidebar / --sidebar-accent colours: background, border, widths, menu radius / height / active / hover colours, group label colour, floating and inset frames. Per instance: SSidebarProvider({ tokens }). The switches write theme variables, so they restyle the playground above and this gallery's own sidebar.

Approximate web preview — the HarmonyOS rendering is the reference.

API ​

Extracted from the @Param / @Event / @BuilderParam declarations in the library sources.

SSidebarProvider ​

shadcn-vue SidebarProvider: lays out an SSidebar and an SSidebarInset side by side and owns the sidebar state. Responsive without breakpoints: layoutPolicy decides from the provider's own width whether the sidebar is docked, an icon rail or an overlay sheet (default: a sheet when the expanded sidebar does not fit next to minInsetWidth of content; sparkSidebarRailPolicy() adds the rail step).

Props: variant 'sidebar' | 'floating' | 'inset', collapsible 'offcanvas' | 'icon' | 'none', side 'left' | 'right' (render order is flipped for you). open / defaultOpen / onOpenChange control the docked state (persist it by feeding a stored value to defaultOpen and saving in onOpenChange); value / defaultValue / onValueChange / onNavigate the active menu value (no routing built in: wire any router to value + onNavigate). controller lets you own the state object.

PropTypeDefaultDescription
controllerSparkSidebarState | undefinedundefinedyour own state object (headless control); one is created when omitted
open!!boolean | undefinedundefined
defaultOpenbooleantrue
value!!string | undefinedundefined
defaultValuestring''
variantSidebarVariant
SidebarVariant = 'sidebar' | 'floating' | 'inset'
'sidebar'
collapsibleSidebarCollapsible
SidebarCollapsible = 'offcanvas' | 'icon' | 'none'
'offcanvas'
sideSidebarSide
SidebarSide = 'left' | 'right'
'left'
sidebarWidthnumber0docked width (vp); 0 = the --sidebar-width token, else 256 (shadcn 16rem)
sidebarWidthMobilenumber0sheet width (vp); 0 = the --sidebar-width-mobile token, else 288
sidebarWidthIconnumber0icon rail width (vp); 0 = the --sidebar-width-icon token, else 48
tokensSidebarTokens{}per-instance look (ADR D13): instance → --sidebar-* component tokens → shadcn recipe
minInsetWidthnumber360the main area should keep at least this width (vp); input of layoutPolicy
safeAreabooleanfalsepad the window safe area (immersive pages, see SparkProvider immersive)
closeOnSelectbooleantrueclose the sheet when a menu button is activated
motionKeystring'sidebar'motion key of the collapse animation and the sheet
EventTypeDefaultDescription
onOpenChange(open: boolean) => void() => { }
onValueChange(value: string) => void() => { }
onNavigate(value: string) => void() => { }every activation of a menu value, even the current one (router hook: re-tap = pop to root)
onOpenMobileChange(open: boolean) => void() => { }the overlay sheet opened / closed
onPresentationChange(presentation: SidebarPresentation) => void
SidebarPresentation = 'docked' | 'rail' | 'sheet'
() => { }the layout policy switched between docked / rail / sheet
layoutPolicySparkSidebarLayoutPolicysparkSidebarDefaultPolicydecides docked / rail / sheet from the measured width (replaceable)

Slots: children (trailing closure)

SSidebar ​

shadcn-vue Sidebar. Docked: a column whose width animates between expanded / icon rail / hidden (provider motionKey, default 'sidebar'). Rail: the icon rail, plus the full sidebar as a sheet when toggled. Sheet: an overlay sheet from side (the SparkUI overlay base, so back gesture, backdrop tap and focus work like SSheet).

Slots: children (trailing closure)

SSidebarHeader ​

Top section (brand, team switcher…): flex flex-col gap-2 p-2.

Slots: children (trailing closure)

SSidebarFooter ​

Slots: children (trailing closure)

SSidebarContent ​

Scrollable middle section; takes the remaining height between header and footer.

Slots: children (trailing closure)

SSidebarGroup ​

Slots: children (trailing closure)

SSidebarGroupContent ​

Slots: children (trailing closure)

SSidebarGroupLabel ​

h-8 px-2 text-xs font-medium text-sidebar-foreground/70; folds away on the icon rail.

PropTypeDefaultDescription
textResourceStr''

SSidebarMenu ​

Slots: children (trailing closure)

SSidebarMenuItem ​

Slots: children (trailing closure)

SSidebarMenuButton ​

shadcn-vue SidebarMenuButton: icon + text (+ badge, + trailing slot). Active when isActive is true, or when value equals the provider's value. Activating calls onSelect, updates the provider value and closes the mobile sheet. On the icon rail only the icon stays visible (the label is clipped by the animating width).

Pressed feedback comes from the system pressed state (SparkRowPressModifier.applyPressedAttribute) and the active highlight switches instantly — never animated.

PropTypeDefaultDescription
textResourceStr''
iconstring | undefinedundefined
valuestring''identifies the button for the provider's value
isActiveboolean | undefinedundefinedexplicit active state (overrides value matching)
badgestring | undefinedundefinedshort count / label on the trailing edge (shadcn SidebarMenuBadge)
sizingSidebarMenuButtonSize
SidebarMenuButtonSize = 'default' | 'sm' | 'lg'
'default'
variantSidebarMenuButtonVariant
SidebarMenuButtonVariant = 'default' | 'outline'
'default'
disabledbooleanfalse
EventTypeDefaultDescription
onSelect() => void() => { }

Slots: children (trailing closure)

SSidebarSeparator ​

h-px mx-2 bg-sidebar-border

No props.

SSidebarTrigger ​

shadcn-vue SidebarTrigger: ghost icon button that toggles the sidebar (the sheet when narrow, the docked collapse otherwise). Works anywhere inside the provider (e.g. in STopbar).

PropTypeDefaultDescription
labelResourceStr$r('app.string.spark_toggle_sidebar')
iconstring | undefinedundefinedlucide icon; defaults to panel-left / panel-right by side
controllerSparkSidebarState | undefinedundefinedtoggle this controller instead of the nearest provider (a trigger outside the provider)

SSidebarInset ​

shadcn-vue SidebarInset: the main area next to the sidebar. Takes the remaining width; with variant 'inset' it becomes a rounded, raised card inside the sidebar-coloured frame.

Slots: children (trailing closure)

MIT Licensed