Sidebar
Composable dashboard sidebar with expanded, collapsed, and mobile navigation.
Overview
Sidebar provides application-shell structure and behavior: a persistent desktop rail, an icon-collapsed mode, and a mobile navigation surface. You compose the navigation content — links, buttons, badges, headers, and footers.
It intentionally does not own routing, page layouts, or a configuration-only items API. Use SidebarMenuButton asChild with your own anchors or framework links.
Mobile navigation composes the existing Drawer primitive so Sheet can replace or wrap that surface later without rewriting the sidebar API. Install with vinyaas add sidebar or via vinyaas add --catalog dashboard.
Preview
Installation
Add the component with the CLI. It copies the files in and installs anything they depend on.
npx vinyaas add sidebarUsage
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar";
function HomeIcon() {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
aria-hidden="true"
className="size-4 shrink-0"
>
<path d="M4 10.5 12 4l8 6.5V20a1 1 0 0 1-1 1h-5v-6H10v6H5a1 1 0 0 1-1-1z" />
</svg>
);
}
export function AppShell() {
return (
<SidebarProvider>
<Sidebar>
<SidebarHeader>
<div className="flex items-center gap-2 px-2 py-1.5">
<span className="bg-sidebar-primary text-sidebar-primary-foreground flex size-6 shrink-0 items-center justify-center rounded-md text-[10px] font-semibold">
<span className="truncate text-sm font-medium group-data-[collapsible=icon]:hidden">
Acme
</span>
A
</span>
</div>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Workspace</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive tooltip="Dashboard">
<HomeIcon />
<span>Dashboard</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
<SidebarInset>
<div className="border-border flex items-center gap-2 border-b p-3">
<SidebarTrigger />
<p className="text-sm font-medium">Overview</p>
</div>
</SidebarInset>
</SidebarProvider>
);
}Examples
Dashboard
Grouped navigation, an active item, main content, and an account footer.
Collapsed
Icon mode keeps controls usable. Labels stay discoverable via accessible names and tooltips.
Composition
Custom header/footer content and badges. Prefer asChild with anchors for routing.
In practice
Start collapsed on dense dashboards: the icon rail stays usable, tooltips name each item, and the inset content stays independent of routing.
API
Other attributes for the underlying element are passed through.
| Name | Type | Default | Description |
|---|---|---|---|
open / defaultOpen / onOpenChange | boolean | (open: boolean) => void | defaultOpen = true | SidebarProvider: controlled or uncontrolled expanded state on desktop. |
openMobile / defaultOpenMobile / onOpenMobileChange | boolean | (open: boolean) => void | — | SidebarProvider: controlled or uncontrolled mobile drawer open state. |
keyboardShortcut | string | false | "b" | SidebarProvider: ⌘/Ctrl + key toggles the sidebar. Pass false to disable. Ignored inside inputs. |
collapsible | "icon" | "offcanvas" | "none" | "icon" | Sidebar: desktop collapse behavior. icon keeps a usable rail; offcanvas hides off-screen. |
side | "left" | "right" | "left" | Sidebar: which edge hosts the desktop rail and mobile drawer. |
asChild | boolean | false | SidebarMenuButton: merge styles onto a child element such as an anchor. |
isActive | boolean | false | SidebarMenuButton: marks the current navigation item. |
tooltip | ReactNode | — | SidebarMenuButton: shown when collapsed on desktop. Also used as aria-label when it is a string. |
useSidebar() | { state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar } | — | Hook for reading and toggling sidebar state from descendants. |
Accessibility
Sidebar navigation is a landmark (nav labelled "Sidebar"). Menu actions are native buttons or consumer links. There is no focus trap on the desktop rail.
- Tab reaches the trigger and menu controls. Enter activates links and buttons; Space activates button controls.
- Collapsed icon mode keeps items in the tab order. Labels stay in the accessibility tree (
sr-onlytext and/oraria-labelfrom string tooltips). Tooltips are not the only source of naming. - Focus-visible rings use
--sidebar-ring/--ringtokens. - Mobile open state uses Drawer: Escape closes, focus moves into the surface, and focus returns to the trigger on close.
- Width transitions respect
prefers-reduced-motionviamotion-reduce:transition-none. - Optional keyboard shortcut: ⌘/Ctrl+B (configurable; disable with
keyboardShortcut={false}). It does not fire while typing in inputs.