docs/components/sidebar
Sections
Components
Utilities
Forms
Hooks
Copy Markdown
A sidebar for navigation and app structure.
For full-page examples that wire the sidebar:
Browse the Sidebar Components (soon).
CLIManual
pnpmbunnpmyarn
pnpm dlx shadcn@latest add @shark/sidebar
SidebarProvider
├── Sidebar
│ ├── SidebarHeader
│ ├── SidebarContent
│ │ └── SidebarGroup
│ │ ├── SidebarGroupLabel
│ │ ├── SidebarGroupAction
│ │ ├── SidebarGroupContent
│ │ └── SidebarMenu
│ │ ├── SidebarMenuItem
│ │ │ ├── SidebarMenuButton
│ │ │ ├── SidebarMenuAction
│ │ │ └── SidebarMenuBadge
│ │ └── SidebarMenuSub
│ │ ├── SidebarMenuSubButton
│ │ └── SidebarMenuSubItem
│ ├── SidebarFooter
│ ├── SidebarSeparator
│ └── SidebarInput
├── SidebarTrigger
├── SidebarRail
└── SidebarInset
Piecing a Sidebar together usually means:
<SidebarProvider> — Owns open/collapsed state, mobile sheet state, and keyboard shortcut handling.<Sidebar> — Fixed (or sheet) column: placement, variant, and collapsible behavior.<SidebarHeader> / <SidebarFooter> — Pin content to the top or bottom of the column.<SidebarContent> — Scrollable middle; optional scrollFade on the inner scroll area.<SidebarGroup> — Labeled section inside the scroll region.<SidebarTrigger> — Icon/control wired to useSidebar().toggleSidebar.<SidebarInset> — Main document column beside the sidebar (required for variant="inset").Optional helpers: <SidebarRail> (edge hit target), the <SidebarMenu> building blocks (submenus, badges, skeletons, actions)—same roles as in the upstream doc.
<SidebarHeader />
<SidebarGroup />
<SidebarGroup />
<SidebarContent />
<SidebarFooter />
<Sidebar />
Sidebar Anatomy
app/layout.tsx keeps the provider at the top level:
app/layout.tsx
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/app-sidebar";
export default function Layout({ children }: React.PropsWithChildren) {
return (
<SidebarProvider>
<AppSidebar />
<main>
<SidebarTrigger />
{children}
</main>
</SidebarProvider>
);
}
The sidebar column itself is usually a dedicated module:
components/app-sidebar.tsx
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarHeader,
} from "@/components/ui/sidebar";
export function AppSidebar() {
return (
<Sidebar>
<SidebarHeader />
<SidebarContent>
<SidebarGroup />
<SidebarGroup />
</SidebarContent>
<SidebarFooter />
</Sidebar>
);
}
Use the onOpenChange prop to control the sidebar open state.
"use client";
import React from "react";
import { Sidebar, SidebarProvider } from "@/components/ui/sidebar";
export const ControlledLayout = () => {
const [open, setOpen] = React.useState(false);
return (
<SidebarProvider onOpenChange={setOpen} open={open}>
<Sidebar />
</SidebarProvider>
);
}
Sidebar uses CSS variables to style the sidebar. See the full palette in Colors.
--sidebar — Sidebar background--sidebar-foreground — Sidebar text--sidebar-primary — Active item background--sidebar-accent — Hover and active states--sidebar-border — Sidebar borders--sidebar-ring — Focus rings
Learn more about styling in the Styling section.
React to collapse mode and peer state with utilities.
<Sidebar collapsible="icon">
<SidebarContent>
<SidebarGroup className="group-data-[collapsible=icon]:hidden" />
</SidebarContent>
</Sidebar>
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuAction className="peer-data-[active=true]/menu-button:opacity-100" />
</SidebarMenuItem>
The toggle listens for ⌘B on macOS and Ctrl+B elsewhere (SIDEBAR_KEYBOARD_SHORTCUT in source). Match that expectation in shortcuts or help copy.
SidebarProvider must wrap any tree that calls useSidebar or renders Sidebar / SidebarTrigger. It forwards standard div props (including className and style).
| Name | Type | Description |
|---|---|---|
defaultOpen | boolean | Initial open state on desktop (true by default). |
open | boolean | Controlled open flag. |
onOpenChange | (open: boolean) => void | Fires when the open flag changes. |
Defaults live beside SIDEBAR_WIDTH and SIDEBAR_WIDTH_MOBILE inside the registry file. Override per layout with CSS variables on the provider:
<SidebarProvider
style={
{
"--sidebar-width": "20rem",
"--sidebar-width-mobile": "20rem",
} as React.CSSProperties
}
>
<Sidebar />
</SidebarProvider>
| Attribute | Default |
|---|---|
--sidebar-width | 16rem |
--sidebar-width-mobile | 18rem |
--sidebar-width-icon | 3rem |
Primary column component: desktop fixed strip + mobile sheet (via Sheet).
| Property | Type | Description |
|---|---|---|
placement | `"left" | "right"` |
variant | `"sidebar" | "floating" |
collapsible | `"offcanvas" | "icon" |
| Mode | Behavior |
|---|---|
offcanvas | Collapses off-screen; rail can recall it. |
icon | Shrinks to an icon rail. |
none | Fixed width; no collapse animation. |
With variant="inset" , park page content in SidebarInset so padding, radius, and background line up with the inset shell:
<SidebarProvider>
<Sidebar variant="inset" />
<SidebarInset>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>
import { useSidebar } from "@/components/ui/sidebar";
export function Example() {
const {
state,
open,
setOpen,
openMobile,
setOpenMobile,
isMobile,
toggleSidebar,
} = useSidebar();
}
| Property | Type | Description |
|---|---|---|
state | `"expanded" | "collapsed"` |
open | boolean | Desktop drawer expanded. |
setOpen | (open: boolean) => void | Set desktop open. |
openMobile | boolean | Mobile sheet visibility. |
setOpenMobile | (open: boolean) => void | Control the sheet. |
isMobile | boolean | Breakpoint-derived mobile flag. |
toggleSidebar | () => void | Toggles desktop or mobile as appropriate. |
Sticky top region—common pattern: workspace switcher or brand row inside a SidebarMenu.
<Sidebar>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton>
<span>Select workspace</span>
<ChevronDown className="ms-auto" />
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
</Sidebar>
Wire menus or Menu triggers to SidebarMenuButton as needed.
Sticky bottom slot—profile, settings entry, sign-out, etc.
<Sidebar>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton>
<User2 /> Username
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>
Wraps scrollable groups. Pass scrollFade when you want a fade on vertical overflow (see component props in source).
<Sidebar>
<SidebarContent>
<SidebarGroup />
<SidebarGroup />
</SidebarContent>
</Sidebar>
| Attribute | Default |
|---|---|
--fade-size | 3rem |
A titled block with optional SidebarGroupAction and SidebarGroupContent.
<SidebarGroup>
<SidebarGroupLabel>Application</SidebarGroupLabel>
<SidebarGroupAction>
<Plus /> <span className="sr-only">Add project</span>
</SidebarGroupAction>
<SidebarGroupContent />
</SidebarGroup>
Collapsible groups pair with Collapsible. SidebarGroupLabel is a plain label slot—keep the trigger beside it (or fold the pattern into SidebarMenu like the preview at the top of this page):
<Collapsible className="group/collapsible" defaultOpen>
<SidebarGroup>
<div className="flex items-center gap-2 px-2">
<SidebarGroupLabel className="flex-1 px-0">Help</SidebarGroupLabel>
<CollapsibleTrigger
className="rounded-md p-1.5 outline-none ring-sidebar-ring focus-visible:ring-2"
type="button"
>
<ChevronDown className="size-4 transition-transform group-data-[state=open]/collapsible:rotate-180" />
</CollapsibleTrigger>
</div>
<CollapsibleContent>
<SidebarGroupContent />
</CollapsibleContent>
</SidebarGroup>
</Collapsible>
Row list inside a group—map data to SidebarMenuItem / SidebarMenuButton.
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
Default renders a Button ; use asChild for links. isActive highlights the active route; tooltip surfaces collapsed labels.
<SidebarMenuButton asChild isActive>
<a href="#">Home</a>
</SidebarMenuButton>
Icon-only or compact action aligned to a row. Use showOnHover to reveal on row hover.
<SidebarMenuItem>
<SidebarMenuButton asChild>
<a href="#">
<Home />
<span>Home</span>
</a>
</SidebarMenuButton>
<SidebarMenuAction>
<Plus /> <span className="sr-only">Add project</span>
</SidebarMenuAction>
</SidebarMenuItem>
Nested list hanging off a parent item.
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton href="#">Child</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</SidebarMenuItem>
Small count or pill beside a row.
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuBadge>24</SidebarMenuBadge>
</SidebarMenuItem>
Placeholder rows while async routes or prefs load.
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton />
</SidebarMenuItem>
))}
</SidebarMenu>
Prebuilt toggle wired to context—drop next to your page title or in a toolbar.
import { useSidebar } from "@/components/ui/sidebar";
export function CustomTrigger() {
const { toggleSidebar } = useSidebar();
return <button type="button" onClick={toggleSidebar}>Toggle</button>;
}
Prefer SidebarTrigger when you want the default styling and accessibility bundle.
Draggable edge control that expands or collapses the desktop rail—place after header/content/footer inside Sidebar.
<Sidebar>
<SidebarHeader />
<SidebarContent>
<SidebarGroup />
</SidebarContent>
<SidebarFooter />
<SidebarRail />
</Sidebar>
[
Previous page
Sheet ](/docs/components/sheet)[
Next page
Signature Pad ](/docs/components/signature-pad)
On This Page
InstallationAnatomyStructureUsageControlledThemingVariable ReferenceStylingKeyboard shortcutAPI ReferenceSidebarProviderWidthSidebarCollapsible modesuseSidebarSidebarHeaderSidebarFooterSidebarContentSidebarGroupSidebarMenuSidebarMenuButtonSidebarMenuActionSidebarMenuSubSidebarMenuBadgeSidebarMenuSkeletonSidebarTriggerSidebarRail