Sidebar
BetaBeta — the props contract is close to final, but small breaking changes can still land.Responsive app sidebar with resizing, offcanvas and icon collapse, mobile navigation, and nested menus.
Examples
Floating mobile menu
Use SidebarMobileNav variant="floating" with SidebarMobileTrigger when a persistent destination bar is not useful. The labelled Menu button opens the same complete navigation. Both presentations share SidebarProvider, support safe areas, reserve content space, and stay inside a layout="contained" preview. Keep the navigation definition and route matching in your application; use render for your router links.
Resizable sidebar
Add SidebarRail with resizable to drag or use arrow keys to resize. Drag below minWidth to collapse; click or press Enter to toggle. SidebarProvider accepts defaultWidth or width/onWidthChange in pixels. Keep saved preferences in your application.
Right sidebar
Use side="right" for navigation on the other edge. The rail follows that edge for pointer and keyboard resizing. This example controls width through SidebarProvider.
Floating sidebar
Use variant="floating" to give the navigation its own bordered surface. On mobile it opens as an edge sheet with the same navigation.
Inset workspace
Use variant="inset" with SidebarInset to frame the content inside the workspace. Each preview contains one complete app shell; use its trigger to collapse the sidebar or open it on mobile.
Composition
Shared mobile navigation
Declare destinations and their behavior once in the application. Render the same items for the desktop sidebar and mobile primary destinations; Menu opens the existing sidebar content. The provider shares visibility, while the application owns route selection.
- <SidebarProvider>
- <Sidebar>
- mobileVariant="drawer"; complete navigation
- <SidebarContent>
- <SidebarMenu />
- <SidebarContent>
- mobileVariant="drawer"; complete navigation
- <SidebarInset>
- route content
- <SidebarMobileNav>
- <SidebarMobileNavItem />
- <SidebarMobileTrigger />
- <Sidebar>
Application shell
- <SidebarProvider>
- <Sidebar>
- <SidebarHeader />
- <SidebarContent>
- <SidebarGroup>
- <SidebarGroupLabel />
- <SidebarGroupContent>
- <SidebarMenu>
- <SidebarMenuItem>
- <SidebarMenuButton />
- <SidebarMenuBadge />
- <SidebarMenuAction />
- <SidebarMenuSub>
- <SidebarMenuItem>
- <SidebarMenuButton />
- <SidebarMenuItem>
- <SidebarMenuItem>
- <SidebarMenu>
- <SidebarGroup>
- <SidebarFooter />
- <SidebarRail />
- <SidebarInset>
- <SidebarTrigger />
- <Sidebar>
Collapsible navigation group
- <SidebarProvider>
- <Sidebar>
- <SidebarContent>
- <SidebarGroup>
- <SidebarMenu>
- <SidebarMenuItem>
- <Collapsible>
- <SidebarMenuButton>
- render prop
- <CollapsibleTrigger />
- render prop
- <CollapsibleContent>
- <SidebarMenuSub>
- <SidebarMenuItem>
- <SidebarMenuButton />
- <SidebarMenuItem>
- <SidebarMenuSub>
- <SidebarMenuButton>
- <Collapsible>
- <SidebarMenuItem>
- <SidebarMenu>
- <SidebarGroup>
- <SidebarContent>
- <Sidebar>
Installation
The Control UI source installs this primitive from src/registry/sources/control-ui/ui/sidebar.tsx.
Dependencies
src/registry/hooks/use-mobile.tsHooksrc/registry/sources/control-ui/ui/sidebar-menu.tsxSupportsrc/registry/sources/control-ui/ui/sidebar-mobile.tsxSupportsrc/registry/sources/control-ui/ui/sidebar-provider.tsxSupportsrc/registry/sources/control-ui/ui/sidebar-resize-rail.tsxSupportsrc/registry/sources/control-ui/ui/sheet.tsxSkinbuttonPrimitiveDrawerdrawerPrimitiveScroll areascroll-areaPrimitiveTooltiptooltipPrimitiveTrack highlighttrack-highlightPrimitiveRaw code
Primary installed source
Knobs
Typed custom properties the recipe paints with. Set one on the root — style, a utility class, or a skin — and every slot inherits it.
--cui-sidebar-* · 59 knobsHow the cascade resolves--cui-sidebar-nav-height<length-percentage>4.5rem--cui-sidebar-nav-background<color>var(--sidebar)--cui-sidebar-nav-foreground<color>var(--sidebar-foreground)--cui-sidebar-nav-border-color<color>var(--sidebar-border)--cui-sidebar-nav-border-width<length>var(--control-rim-width)--cui-sidebar-nav-shadow*var(--shadow-md)--cui-sidebar-nav-item-radius<length-percentage>var(--radius-control)--cui-sidebar-nav-item-active-background<color>var(--active-fill)--cui-sidebar-nav-item-active-foreground<color>var(--foreground)--cui-sidebar-nav-gap<length-percentage>calc(var(--spacing) * 3)--cui-sidebar-menu-badge-height<length-percentage>calc(var(--spacing) * 5)--cui-sidebar-menu-badge-background<color>oklch(from var(--sidebar-foreground) l c h / 0.08)--cui-sidebar-menu-badge-foreground<color>var(--sidebar-foreground)--cui-sidebar-rail-divider-background<color>transparent--cui-sidebar-wrapper-background<color>transparent--cui-sidebar-wrapper-background-image*none--cui-sidebar-inner-background<color>var(--sidebar)--cui-sidebar-inner-border-color<color>var(--sidebar-border)--cui-sidebar-inner-background-image*none--cui-sidebar-inner-backdrop-filter*none--cui-sidebar-inner-border-width<length>0px--cui-sidebar-inner-radius<length-percentage>0px--cui-sidebar-inner-shadow*none--cui-sidebar-header-background<color>transparent--cui-sidebar-header-shadow*0 0 transparent--cui-sidebar-inset-background<color>var(--background)--cui-sidebar-inset-background-image*none--cui-sidebar-inset-shadow*var(--shadow-sm)--cui-sidebar-inset-border-color<color>var(--sidebar-border)--cui-sidebar-inset-border-width<length>0px--cui-sidebar-inset-radius<length-percentage>var(--radius-xl)--cui-sidebar-inset-inset<length-percentage>calc(var(--spacing) * 2)--cui-sidebar-menu-button-radius<length-percentage>var(--radius-popup-item)--cui-sidebar-group-label-foreground<color>oklch(from var(--sidebar-foreground) l c h / 0.7)--cui-sidebar-menu-button-foreground<color>var(--sidebar-foreground)--cui-sidebar-menu-button-hover-background<color>var(--hover-fill)--cui-sidebar-menu-button-hover-foreground<color>var(--foreground)--cui-sidebar-menu-button-hover-shadow*0 0 transparent--cui-sidebar-menu-button-active-background<color>var(--active-fill)--cui-sidebar-menu-button-active-foreground<color>var(--foreground)--cui-sidebar-menu-button-active-shadow*0 0 transparent--cui-sidebar-menu-button-track-active-foreground<color>var(--foreground)--cui-sidebar-menu-button-height<length-percentage>var(--control-h-md)--cui-sidebar-menu-button-padding-block<length-percentage>0px--cui-sidebar-menu-button-padding-inline<length-percentage>calc(var(--sidebar-width-icon) / 2 - var(--spacing) * 4)--cui-sidebar-menu-button-font-size<length-percentage>var(--text-body)--cui-sidebar-menu-button-font-weight<number>400--cui-sidebar-menu-button-active-font-weight<number>500--cui-sidebar-group-label-font-size<length-percentage>var(--text-caption)--cui-sidebar-group-label-font-weight<number>500--cui-sidebar-group-label-letter-spacing<length>0em--cui-sidebar-group-label-text-transformnone | uppercase | lowercase | capitalizenone--cui-sidebar-group-label-padding<length-percentage>+0px calc(var(--spacing) * 2)--cui-sidebar-skip-link-background<color>var(--background)--cui-sidebar-skip-link-foreground<color>var(--foreground)--cui-sidebar-skip-link-radius<length-percentage>var(--radius-control)--cui-sidebar-skip-link-shadow*var(--shadow-md)--cui-sidebar-menu-button-background<color>transparent--cui-sidebar-menu-button-shadow*0 0 transparent