Menu
Sidebar
The navigation column of an admin screen: groups of links that collapse to icons or hide entirely.
Import
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger } from "@booleanpress/ui/sidebar"Usage
A sidebar is a family of parts. SidebarProvider holds the state and wraps both the Sidebar and the page beside it, SidebarInset. Put a SidebarTrigger where people expect the toggle, usually in the page header.
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupLabel,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarTrigger,
} from "@booleanpress/ui/sidebar"
export function AdminLayout({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider>
<Sidebar collapsible="icon">
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Delivery</SidebarGroupLabel>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton asChild isActive tooltip="Email log">
<a href="/logs">Email log</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
</Sidebar>
<SidebarInset>
<header>
<SidebarTrigger />
</header>
{children}
</SidebarInset>
</SidebarProvider>
)
}collapsible sets what the toggle does: "icon" shrinks it to a rail of icons (give each SidebarMenuButton a tooltip and keep the label in a span), "offcanvas" (the default) slides it out of view, "none" makes it fixed. side is left or right; variant is sidebar, floating or inset.
The open or collapsed state is kept in the browser's local storage under the provider's sidebarStorageKey (one key per product), and read back on the next visit. It starts open unless defaultOpen={false}; a saved choice wins over the default. On the first render the state is defaultOpen, and the saved value applies right after. Control it with open and onOpenChange. useSidebar() returns state, open, setOpen, isMobile, openMobile, setOpenMobile and toggleSidebar. Ctrl+B or ⌘B toggles it. Below 768 px wide the sidebar becomes a sheet opened by the same trigger.
The sidebar positions itself against the window (position: fixed) and the page is at least a window tall, so it belongs in the app's outer layout, once.
Examples
Admin layout
Icon-collapsible sidebar with groups, an active item, a badge, a collapsible submenu, and the trigger in the page header.
Starting collapsed
defaultOpen={false} starts it as a rail of icons until someone opens it.
On the right
side="right" with collapsible="offcanvas": a detail panel that slides out of view.
Accessibility
- Semantics
- A plain container: put the links in a
navyourself by passingasChildtoSidebarMenuButtonwith a link, or wrap the sidebar's content in anavwith a name.SidebarMenuButtonrenders abutton;isActivesetsdata-active, notaria-current. Menus areullists. On narrow windows the sidebar is a modal dialog (a sheet) named from the provider'ssidebarTitleand described bysidebarDescription. - Labels
- The trigger and the rail are named from the provider's
toggleSidebarstring. An icon-only button in the collapsed rail is named by its text, which stays in the DOM (hidden visually), and by its tooltip. Passaria-current="page"yourself on the link of the current page. - Focus
- The trigger and every menu button are in the tab order. The rail is a mouse affordance and is not focusable (
tabIndex={-1}). On narrow windows the sheet moves focus in, keeps Tab inside and returns focus to the trigger. - Known limits
isActiveonly styles the item; it does not setaria-current. Set it on the link yourself.- It positions itself against the window, so it cannot sit inside a smaller panel. The examples render each in its own frame.
- A tooltip shows only in the collapsed rail, never when the sidebar is open or on narrow windows.
Keyboard
| Key | Behaviour |
|---|---|
| CtrlB | Toggles the sidebar from anywhere on the page. ⌘B on macOS. |
| EnterSpace | On the trigger or a menu button, presses it. |
| Tab | Moves through the trigger and the menu buttons in order. |
| Escape | On narrow windows, closes the sheet and returns focus to the trigger. |
API
Sidebar
Renders a div and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
collapsible | "none" | "icon" | "offcanvas" | offcanvas | What the toggle does: offcanvas slides it out of view, icon leaves a rail of icons, none keeps it fixed. |
side | "left" | "right" | left | left or right. left by default. |
variant | "inset" | "sidebar" | "floating" | sidebar | sidebar (full height, a border), floating (a card with a margin) or inset (the page sits in a card beside it). |
SidebarContent
Renders a div and passes it every other prop.
SidebarFooter
Renders a div and passes it every other prop.
SidebarGroup
Renders a div and passes it every other prop.
SidebarGroupAction
Renders a button and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render the child element instead, with this part's classes merged onto it. |
SidebarGroupContent
Renders a div and passes it every other prop.
SidebarGroupLabel
Renders a div and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render the child element instead, with this part's classes merged onto it. |
SidebarHeader
Renders a div and passes it every other prop.
SidebarInput
SidebarInset
Renders a main and passes it every other prop.
SidebarMenu
Renders a ul and passes it every other prop.
SidebarMenuAction
Renders a button and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render the child element instead, with this part's classes merged onto it. |
showOnHover | boolean | false | Show it only while the row is hovered or has focus. |
SidebarMenuBadge
Renders a div and passes it every other prop.
SidebarMenuButton
Renders a button and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render the child element instead, such as a link, with this part's classes merged onto it. |
isActive | boolean | false | Marks the current page (data-active) and highlights it. |
tooltip | string | (TooltipContentProps & RefAttributes<HTMLDivElement>) | Text, or TooltipContent props, shown beside the button while the sidebar is collapsed to icons. |
SidebarMenuItem
Renders a li and passes it every other prop.
SidebarMenuSkeleton
Renders a div and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
showIcon | boolean | false | Draw a square where the icon goes. |
SidebarMenuSub
Renders a ul and passes it every other prop.
SidebarMenuSubButton
Renders a a and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render the child element instead, such as a router link. |
isActive | boolean | false | Marks the current page and highlights it. |
size | "sm" | "md" | md | sm or md. md by default. |
SidebarMenuSubItem
Renders a li and passes it every other prop.
SidebarProvider
Renders a div and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | true | Whether it starts open. true by default; a choice saved in local storage wins. |
onOpenChange | ((open: boolean) => void) | Called with true or false when it opens or collapses. | |
open | boolean | Whether it is open, when you control it. Pair it with onOpenChange. |
SidebarRail
Renders a button and passes it every other prop.
SidebarSeparator
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | ||
decorative | boolean | Whether or not the component is purely decorative. When true, accessibility-related attributes are updated so that that the rendered element is removed from the accessibility tree. | |
orientation | "horizontal" | "vertical" | vertical | Either vertical or horizontal. Defaults to horizontal. |
SidebarTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean |
Also exported: useSidebar, a hook for the parts' shared state; call it inside the component's provider.
Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.
Data attributes: data-slot="sidebar" (Sidebar), data-slot="sidebar-content" (SidebarContent), data-slot="sidebar-footer" (SidebarFooter), data-slot="sidebar-group" (SidebarGroup), data-slot="sidebar-group-action" (SidebarGroupAction), data-slot="sidebar-group-content" (SidebarGroupContent), data-slot="sidebar-group-label" (SidebarGroupLabel), data-slot="sidebar-header" (SidebarHeader), data-slot="sidebar-input" (SidebarInput), data-slot="sidebar-inset" (SidebarInset), data-slot="sidebar-menu" (SidebarMenu), data-slot="sidebar-menu-action" (SidebarMenuAction), data-slot="sidebar-menu-badge" (SidebarMenuBadge), data-slot="sidebar-menu-button" (SidebarMenuButton), data-slot="sidebar-menu-item" (SidebarMenuItem), data-slot="sidebar-menu-skeleton" (SidebarMenuSkeleton), data-slot="sidebar-menu-sub" (SidebarMenuSub), data-slot="sidebar-menu-sub-button" (SidebarMenuSubButton), data-slot="sidebar-menu-sub-item" (SidebarMenuSubItem), data-slot="sidebar-wrapper" (SidebarProvider), data-slot="sidebar-rail" (SidebarRail), data-slot="sidebar-separator" (SidebarSeparator), data-slot="sidebar-trigger" (SidebarTrigger), and data-state, data-collapsible, data-variant, data-side, data-size, data-active.
Provider strings: sidebarTitle, sidebarDescription, toggleSidebar (BooleanUIProvider's strings).
Theming
The sidebar has its own tokens: --sidebar, --sidebar-foreground, --sidebar-accent, --sidebar-accent-foreground, --sidebar-border and --sidebar-ring. Width is --sidebar-width (16rem) and --sidebar-width-icon (3rem) on the provider; override them with style. On narrow windows the sheet is 18rem wide.
The tokens its classes read. Change their values in your theme, and it follows.
| Token | Used for |
|---|---|
--background | background |
--sidebar | background |
--sidebar-accent | background |
--sidebar-accent-foreground | text |
--sidebar-border | border, background |
--sidebar-foreground | text |
--sidebar-ring | focus ring |