Search documentation
Search pages, components, blocks, styles and colours.
An app sidebar that drags to resize, peeks out when collapsed and becomes a drawer on mobile.
The sidebar is the frame of an app: a header, scrolling sections of rows, a footer, and the main area beside it. Drag its edge to resize it, click the edge or press ⌘B to collapse it, and hover the collapsed edge to peek at it. Below 768px it becomes a drawer.
Rows share one hover highlight that glides from row to row, and the current row's background slides when the selection moves. Sections collapse like an accordion, rows take status dots, badges and actions, and the toggle's panel glyph animates between its two states.
Installation
Usage
import { cookies } from "next/headers"
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar"
import { AppSidebar } from "@/components/app-sidebar"
export default async function Layout({
children,
}: {
children: React.ReactNode
}) {
const cookieStore = await cookies()
const defaultOpen = cookieStore.get("sidebar_state")?.value !== "false"
return (
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar />
<SidebarInset>
<header className="flex h-12 items-center px-3">
<SidebarTrigger />
</header>
{children}
</SidebarInset>
</SidebarProvider>
)
}"use client"
import { InboxIcon, SearchIcon } from "lucide-react"
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarRail,
} from "@/components/ui/sidebar"
export function AppSidebar() {
return (
<Sidebar>
<SidebarHeader />
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Workspace</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton icon={InboxIcon} isActive>
<span>Inbox</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton icon={SearchIcon}>
<span>Search</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter />
<SidebarRail />
</Sidebar>
)
}The provider saves the open state to the sidebar_state cookie, so a server layout can read it back into defaultOpen and the sidebar renders in the right state on the first paint.
Composition
SidebarProvider
├── Sidebar
│ ├── SidebarHeader
│ ├── SidebarContent
│ │ └── SidebarGroup
│ │ ├── SidebarGroupLabel
│ │ ├── SidebarGroupAction (or SidebarGroupActions)
│ │ └── SidebarGroupContent
│ │ └── SidebarMenu
│ │ └── SidebarMenuItem
│ │ ├── SidebarMenuButton
│ │ ├── SidebarMenuAction (or SidebarMenuActions)
│ │ ├── SidebarMenuBadge
│ │ └── SidebarMenuSub
│ │ └── SidebarMenuSubItem
│ │ └── SidebarMenuSubButton
│ ├── SidebarFooter
│ └── SidebarRail
└── SidebarInset
└── SidebarTrigger- SidebarProvider holds the state: open or collapsed, the mobile drawer, the live width, the shortcut and peek.
- Sidebar is the panel.
SidebarInsetis the main area next to it. - SidebarHeader and SidebarFooter stay put; SidebarContent scrolls between them, fading its rows out at the edges.
- SidebarGroup is a section: a label, optional actions, and its rows.
- SidebarMenu is a list of rows. Each SidebarMenuItem holds a SidebarMenuButton and, optionally, actions, a badge and a sub-menu.
- SidebarRail is the handle on the sidebar's edge. SidebarTrigger toggles the sidebar from anywhere inside the provider.
Examples
Floating
variant="floating" lifts the panel into a rounded card. The main demo above uses variant="inset", which turns the main area into the card instead.
Collapsible sections
collapsible on SidebarGroup turns its label into a toggle for everything after it. A chevron appears on hover and stays visible while the section is closed. SidebarGroupActions holds up to three actions on the label row.
Status dots
status draws a dot in the icon column: unread is filled, idle is a ring, and active is filled and marks the row as the current one. Screen readers hear "Unread" before an unread row. For a dot without that meaning, use dot="filled" or dot="ring".
Actions and badges
A badge sits at the end of the row and turns semibold on the active row. An action sits beside it. showOnHover hides actions until the row is hovered or focused, and SidebarMenuActions groups several. The label always stops short of them.
Nested rows
open on SidebarMenuSub collapses the sub-tree on the moderate spring; wire it to the parent row. The hover highlight glides from the parent into its children, and arrow keys skip a closed sub-tree.
Peek
With peek="hover" on the provider, a collapsed sidebar floats out as a card when the pointer rests on its edge or on the trigger, and tucks away when the pointer leaves. peek="click" opens it on a click of the edge instead. Escape or a press outside dismisses it; clicking a trigger pins it open.
Put a SidebarTrigger in the sidebar's header as well: the card covers the main area's trigger while it is out.
Right side
side="right" puts the sidebar on the right edge, and the toggle glyph mirrors. Here the shortcut is a bare ].
Collapse to icons
collapsible="icon" keeps a 48px column of icons when collapsed. Give each row a tooltip so its name shows on hover.
Controlled
const [open, setOpen] = React.useState(false)
return (
<SidebarProvider open={open} onOpenChange={setOpen}>
<Sidebar />
</SidebarProvider>
)Resize and collapse
SidebarRail is the sidebar's edge handle. Drag it to resize between 160px and 360px; drag past the minimum and the sidebar collapses (pull back before letting go to keep it open). Click it to collapse. Its tooltip explains both and shows the shortcut. Leave it out for a fixed width.
The starting width is width on the provider (16rem by default). useSidebar() returns the live width and setWidth, so you can save it:
const { width } = useSidebar()
React.useEffect(() => {
localStorage.setItem("sidebar-width", width)
}, [width])Keyboard shortcut
⌘B (Ctrl B on Windows and Linux) toggles the sidebar. Set
shortcut on the provider to change it: "mod+b" for the default, or a bare
key such as "[", which is ignored while typing in a field. shortcut={null}
turns it off. With providers nested, the innermost one around the focus answers.
Motion
- Opening and closing move on the moderate tier: the spring to open (210ms), the exit tween to close (160ms). A peeking card slides in on the same spring.
- While the rail is dragged, the width follows the pointer with no transition.
- Sections and sub-trees collapse on the moderate tier, height and opacity together.
- The hover highlight glides on the fast tier; the active row's background slides on the moderate one.
- The toggle's panel glyph morphs on the moderate spring (browsers that can't animate an SVG path swap it).
- With reduced motion, the sidebar, the card and the glyph change at once, and collapses only fade.
API Reference
SidebarProvider
persist saves the desktop state to the sidebar_state cookie for 7 days; the drawer's state isn't saved. Peek works with collapsible="offcanvas". The width is also published as --sidebar-width; a --sidebar-width set in style is used as the starting width.
Sidebar
bordered draws the sidebar variant's inner-edge border. With the inset variant, wrap the main content in SidebarInset.
useSidebar
SidebarTrigger
A ghost icon button that calls toggleSidebar(). Its tooltip names the action with the shortcut. Pass children to replace the panel glyph. With peek="hover", resting on it while collapsed peeks the sidebar.
SidebarRail
Drag to resize, click to collapse (or expand). It takes <button> props and isn't in the tab order: keyboard users have the trigger and the shortcut.
SidebarGroup
Collapsible groups need a SidebarGroupLabel child; everything after it (other than the header actions) collapses. You can still wrap a group in a Collapsible instead.
SidebarMenuButton
To render a link, use the render prop (render={<Link href="/inbox" />}):
<SidebarMenuButton render={<a href="/inbox" />} icon={InboxIcon} isActive>
Inbox
</SidebarMenuButton>icon takes the component itself, so the file that passes it must be a Client Component. In a Server Component, put the icon in the children instead (<InboxIcon /> before the label). tooltip shows only while collapsed to icons.
SidebarMenuAction, SidebarMenuActions
SidebarMenuActions wraps several SidebarMenuActions; set showOnHover on the wrapper. SidebarGroupActions does the same for SidebarGroupActions on a section's label row. Give every action an aria-label.
SidebarMenuSub, SidebarMenuSubButton
Without open the sub-menu is always shown.
SidebarMenuBadge, SidebarMenuSkeleton
SidebarMenuBadge takes <div> props. SidebarMenuSkeleton takes showIcon and renders a loading row.
Data attributes
Theming
The panel uses the sidebar color tokens (--sidebar, --sidebar-foreground, --sidebar-border, --sidebar-ring, …). In the Fabricator style the panel is surface 2, rows are muted at rest and foreground when lit, the hover highlight is bg-hover and the active row bg-active.
Accessibility
- The trigger is a button named "Toggle Sidebar"; its tooltip says whether it will expand or collapse and shows the shortcut.
- The active row has
aria-current="page". Arrow Up and Down move between rows (sub-rows included, closed ones skipped), Home and End jump to the first and last, and Tab still visits each row. A row that opens a menu keeps the menu's own arrow keys. - A collapsible section's label is a button with
aria-expandedandaria-controls. A closed section or sub-tree isinert, so its rows leave the tab order. - Status dots are visual;
status="unread"adds "Unread" for screen readers. - The rail is pointer-only (
tabIndex=-1); the peek edge is a button named "Show sidebar" witharia-expanded. - On mobile the sidebar is a modal drawer: focus is trapped inside, Escape and a press on the scrim close it.
- Every interactive part shows the 1px focus ring. The rail has a resize cursor; rows, actions and labels a pointer.
- With reduced motion the sidebar, peek and glyph change without travel.