Overview
The Sidebar component system provides a full-featured navigation sidebar with:
- Desktop and mobile responsive behavior
- Collapsible states (expanded/collapsed)
- Keyboard shortcuts (Cmd/Ctrl + B)
- Icon-only collapsed mode
- Sheet-based mobile drawer
- Tooltips for collapsed items
- Cookie-based state persistence
Quick Start
import {
SidebarProvider,
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuItem,
SidebarMenuButton,
SidebarInset,
SidebarTrigger,
} from "@repo/ui";
import { Home, Settings } from "lucide-react";
function App() {
return (
<SidebarProvider>
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Navigation</SidebarGroupLabel>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton>
<Home />
<span>Home</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton>
<Settings />
<span>Settings</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
</Sidebar>
<SidebarInset>
<header>
<SidebarTrigger />
</header>
<main>{/* Your page content */}</main>
</SidebarInset>
</SidebarProvider>
);
}
Core Components
The root component that manages sidebar state.
Default open state of the sidebar.
Controlled open state. Use with onOpenChange for controlled mode.
Callback when open state changes.
The main sidebar container.
Which side to display the sidebar.Options: left, right
Visual variant of the sidebar.Options: sidebar, floating, inset
sidebar - Standard attached sidebar
floating - Floating sidebar with rounded corners
inset - Inset sidebar with margin
collapsible
string
default:"offcanvas"
Collapse behavior.Options: offcanvas, icon, none
offcanvas - Slides completely off screen
icon - Collapses to icon-only width
none - Cannot be collapsed
Access sidebar state and controls from any component within SidebarProvider.
import { useSidebar } from "@repo/ui";
function MyComponent() {
const {
state, // "expanded" | "collapsed"
open, // boolean
setOpen, // (open: boolean) => void
openMobile, // boolean
setOpenMobile,// (open: boolean) => void
isMobile, // boolean
toggleSidebar // () => void
} = useSidebar();
return <div>Sidebar is {state}</div>;
}
Container for menu items.
<SidebarMenu>
<SidebarMenuItem>...</SidebarMenuItem>
</SidebarMenu>
Individual menu item wrapper.
Clickable menu button with variants and active states.
Render as a slot for custom elements (e.g., Next.js Link).
Whether this menu item is currently active.
Visual variant.Options: default, outline
Button size.Options: default, sm, lg
Tooltip content shown when sidebar is collapsed.
<SidebarMenuItem>
<SidebarMenuButton tooltip="Dashboard">
<Home />
<span>Dashboard</span>
</SidebarMenuButton>
</SidebarMenuItem>
When collapsed, only the icon shows and hovering displays “Dashboard” tooltip.
Nested submenu container.
Submenu item wrapper.
Submenu button component.
<SidebarMenuItem>
<SidebarMenuButton>
<Settings />
<span>Settings</span>
</SidebarMenuButton>
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton>Profile</SidebarMenuSubButton>
</SidebarMenuSubItem>
<SidebarMenuSubItem>
<SidebarMenuSubButton>Preferences</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</SidebarMenuItem>
Layout Components
Header section at the top of the sidebar.
<Sidebar>
<SidebarHeader>
<div className="flex items-center gap-2">
<Logo />
<span className="font-semibold">My App</span>
</div>
</SidebarHeader>
<SidebarContent>...</SidebarContent>
</Sidebar>
Scrollable content area.
Footer section at the bottom (e.g., user profile).
<Sidebar>
<SidebarContent>...</SidebarContent>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton>
<User />
<span>Profile</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>
Groups related menu items.
Label for a group of items.
Content wrapper for group items.
Action button in group header.
<SidebarGroup>
<SidebarGroupLabel>
Projects
<SidebarGroupAction>
<Plus />
</SidebarGroupAction>
</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>...</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
Additional Components
Main content area container.
<SidebarInset>
<main>{/* Your page content */}</main>
</SidebarInset>
Button to toggle sidebar open/closed.
Hover area for expanding collapsed sidebar.
<Sidebar>
<SidebarContent>...</SidebarContent>
<SidebarRail />
</Sidebar>
Visual separator between sections.
<SidebarContent>
<SidebarGroup>...</SidebarGroup>
<SidebarSeparator />
<SidebarGroup>...</SidebarGroup>
</SidebarContent>
Search input styled for sidebar.
<SidebarHeader>
<SidebarInput placeholder="Search..." />
</SidebarHeader>
Action button on menu items (e.g., dropdown trigger).
Only show the action on hover.
<SidebarMenuItem>
<SidebarMenuButton>Item</SidebarMenuButton>
<SidebarMenuAction showOnHover>
<MoreHorizontal />
</SidebarMenuAction>
</SidebarMenuItem>
Badge/counter on menu items.
<SidebarMenuItem>
<SidebarMenuButton>
<Inbox />
<span>Inbox</span>
</SidebarMenuButton>
<SidebarMenuBadge>12</SidebarMenuBadge>
</SidebarMenuItem>
Loading skeleton for menu items.
Whether to show the icon skeleton.
<SidebarMenu>
<SidebarMenuSkeleton showIcon />
<SidebarMenuSkeleton showIcon />
</SidebarMenu>
Complete Example
import {
SidebarProvider,
Sidebar,
SidebarHeader,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupLabel,
SidebarGroupContent,
SidebarMenu,
SidebarMenuItem,
SidebarMenuButton,
SidebarMenuSub,
SidebarMenuSubItem,
SidebarMenuSubButton,
SidebarInset,
SidebarTrigger,
SidebarSeparator,
} from "@repo/ui";
import {
Home,
Settings,
Users,
FileText,
ChevronDown,
} from "lucide-react";
function AppSidebar() {
return (
<Sidebar>
<SidebarHeader>
<div className="flex items-center gap-2 px-2">
<img src="/logo.svg" className="size-8" />
<span className="font-bold text-lg">MyApp</span>
</div>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Main</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive tooltip="Home">
<Home />
<span>Home</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Users">
<Users />
<span>Users</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarSeparator />
<SidebarGroup>
<SidebarGroupLabel>Settings</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton>
<Settings />
<span>General</span>
<ChevronDown className="ml-auto" />
</SidebarMenuButton>
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton>Profile</SidebarMenuSubButton>
</SidebarMenuSubItem>
<SidebarMenuSubItem>
<SidebarMenuSubButton>Preferences</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton>
<img src="/avatar.jpg" className="size-6 rounded-full" />
<span>John Doe</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>
);
}
function App() {
return (
<SidebarProvider defaultOpen={true}>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 items-center gap-2 border-b px-4">
<SidebarTrigger />
<h1>Page Title</h1>
</header>
<main className="p-6">
{/* Your content */}
</main>
</SidebarInset>
</SidebarProvider>
);
}
Keyboard Shortcuts
The sidebar automatically registers a keyboard shortcut:
- Cmd + B (Mac) or Ctrl + B (Windows/Linux) - Toggle sidebar
State Persistence
The sidebar state is automatically saved to a cookie (sidebar_state) and restored on page reload.
Cookie details:
- Name:
sidebar_state
- Max age: 7 days
- Path:
/
Mobile Behavior
On mobile devices (detected via useIsMobile hook):
- Sidebar renders as a Sheet (drawer)
- Slides in from the side
- Overlays the content
- Controlled via
SidebarTrigger
CSS Variables
The sidebar system uses CSS variables for sizing:
--sidebar-width: 16rem; /* 256px - Desktop width */
--sidebar-width-mobile: 18rem; /* 288px - Mobile width */
--sidebar-width-icon: 3rem; /* 48px - Icon-only width */
Accessibility
- Keyboard navigation support
- Focus management
- ARIA attributes
- Screen reader friendly
- Tooltip support for collapsed items
- Mobile drawer with proper semantics