Sidebar
The sidebar shell. Reads open/collapsed state from {@link useSidebar}, so it
must render inside a {@link SidebarProvider}.
Adapts by viewport and collapsible: an animated docked shell on desktop, a
slide-in Sheet on mobile, or a static column when collapsible="none".
Usage
import { CONTENT_COLUMN_CLASS, HEADER_HEIGHT, Sidebar, SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_KEYBOARD_SHORTCUT, SIDEBAR_TRANSITION, SIDEBAR_WIDTH, SIDEBAR_WIDTH_COOKIE_NAME, SIDEBAR_WIDTH_DEFAULT_PX, SIDEBAR_WIDTH_ICON, SIDEBAR_WIDTH_MAX_PX, SIDEBAR_WIDTH_MIN_PX, SIDEBAR_WIDTH_MOBILE, sidebarAnatomy, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarLayoutHeader, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuLabel, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarMenuSubScrollArea, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, useSidebarResize } from "@desk/ui/components/sidebar";Best practices
Render
Sidebarinside aSidebarProvider— it reads open/collapsed state fromuseSidebarand cannot work outside one.Choose
collapsibledeliberately:offcanvasslides fully away,iconshrinks to a rail,noneis a static column that never adapts to the mobileSheet.
Props
Sidebar
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | ||
| side | "left" | "right" | "left" | Edge the sidebar docks to. |
| variant | "inset" | "sidebar" | "floating" | "sidebar" | Shell style: standard, floating (inset card), or inset (offset content). |
| collapsible | "none" | "icon" | "offcanvas" | "offcanvas" | Collapse behavior: slide off-canvas, shrink to icon rail, or never collapse. |
| resizable | boolean | false | Show a drag handle on the sidebar's inner edge to resize its expanded width (desktop only). Width is clamped, cookie-persisted, and dragging past the collapse threshold magnetically snaps to collapsed. |
SidebarGroupAction
| Prop | Type | Default | Description |
|---|---|---|---|
| tone | "neutral" | "brand" | "danger" | null | undefined | ||
| radius | "rounded" | "square" | "full" | null | undefined | ||
| square | boolean | null | undefined | ||
| emphasis | "link" | "outline" | "solid" | "ghost" | "soft" | "transparent" | null | undefined | ||
| asInput | boolean | null | undefined | ||
| align | "center" | "end" | "start" | null | undefined | ||
| pressed | boolean | null | undefined | ||
| size | ButtonSizeProp | "base" | Size token; |
| asChild | boolean | false | Merge props and styling onto the single child element via Slot. |
| loading | boolean | undefined | false | Show a spinner and disable interaction. |
| loadingLabel | string | undefined | Label shown in place of | |
| icon | IconComponent | undefined | Leading icon (Lucide or SVG component); with no children it becomes an icon-only button. | |
| iconClassName | string | undefined | Extra classes for the leading icon. | |
| suffixIcon | IconComponent | undefined | Trailing icon (Lucide or SVG component) rendered at the end. | |
| suffixIconClassName | string | undefined | Extra classes for the trailing icon. | |
| dot | string | undefined | CSS colour for a status dot; empty/undefined renders no dot. | |
| dotPlacement | "end" | "start" | undefined | "start" | Dot position relative to content. |
| expandable | boolean | undefined | false | Icon-only at rest; expands on hover/focus-visible to reveal the label
to the right of the icon. Collapsed geometry equals the matching icon-*
size, expanded equals the text button of that size — so pass a text
size ( |
| focused | boolean | undefined | Force the focus ring on — for documentation/testing only. | |
| hovered | boolean | undefined | Force the hover state on — for documentation/testing only. | |
| testId | string | ||
| showWhenCollapsed | boolean | Show the action button even when sidebar is collapsed |
SidebarGroupLabel
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean |
SidebarInput
| Prop | Type | Default | Description |
|---|---|---|---|
| testId | string | ||
| radius | "rounded" | "square" | "full" | null | undefined | ||
| size | ResponsiveSize | "xl" (48px) | Field size — a single token or a per-breakpoint object like
|
| hasError | boolean | Force error styling; overrides invalid state inherited from an ancestor Field. | |
| focused | boolean | Force focus ring display (for documentation/testing) |
SidebarLayoutHeader
| Prop | Type | Default | Description |
|---|---|---|---|
| rightOffset | string | Right padding when sidepanel or other overlay is open |
SidebarMenuAction
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | Render the child element as the button (Radix Slot). | |
| showOnHover | boolean | false | Reveal only on row hover/focus (or when open) instead of always. |
SidebarMenuButton
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | Render the child element as the button (Radix Slot), e.g. wrapping a | |
| active | boolean | Marks the row as the current/selected item (sets | |
| tooltip | string | (import("@radix-ui/react-tooltip").TooltipContentProps & React.RefAttributes<HTMLDivElement>) | Tooltip content shown only when the sidebar is collapsed; also becomes the collapsed | |
| tooltipOpen | boolean | Control tooltip visibility. Use | |
| size | "base" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | ||
| variant | "input" | "default" | "outline" | ||
| icon | IconComponent | ||
| collapsedIcon | IconComponent | Icon to show when sidebar is collapsed (overrides icon) | |
| collapsedContent | React.ReactNode | Custom content to show when sidebar is collapsed (e.g., Avatar). Takes precedence over collapsedIcon. | |
| suffixIcon | IconComponent | ||
| forceExpanded | boolean | Keep label/suffix visible even when the sidebar provider is collapsed. | |
| kbd | string[] | ||
| children | React.ReactNode |
SidebarMenuLabel
| Prop | Type | Default | Description |
|---|---|---|---|
| delay | number | 0 | Delay (seconds) before the expand/collapse fade, for staggering labels. |
SidebarMenuSkeleton
| Prop | Type | Default | Description |
|---|---|---|---|
| showIcon | boolean | false | Also render a leading icon placeholder. |
| index | number | Index for deterministic width (prevents SSR hydration mismatch) |
SidebarMenuSubButton
| Prop | Type | Default | Description |
|---|---|---|---|
| kbd | string[] | undefined | Keyboard shortcut keys to display (e.g., ["⌘", "C"]). Ignored if suffixIcon is provided. | |
| children | string | number | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | undefined | Text label for the menu item. Use | |
| asChild | boolean | undefined | Render as child element using Slot | |
| variant | "input" | "default" | "outline" | null | undefined | ||
| tone | "neutral" | "danger" | null | undefined | ||
| inset | boolean | null | undefined | ||
| radius | "rounded" | "square" | "full" | null | undefined | ||
| size | "base" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | null | undefined | ||
| loading | boolean | undefined | Show loading spinner in place of icon | |
| active | boolean | undefined | Whether the item is in an active/selected state | |
| indicator | React.ReactNode | Indicator element for checkbox/radio items (absolutely positioned on left). Automatically enables inset. | |
| checked | boolean | undefined | Checked state. Renders the selectionMode indicator inline. When undefined, no indicator renders. | |
| suffixIcon | LucideIcon | React.ComponentType<React.SVGProps<SVGSVGElement>> | undefined | Icon to display at the end (e.g., checkmark, chevron). Takes precedence over kbd. | |
| avatar | React.ReactNode | Avatar element to display at the start (takes precedence over icon and dot) | |
| suffix | React.ReactNode | Content to display at the end after suffixIcon/kbd (e.g., custom content) | |
| activeIndicator | "none" | "dot" | "check" | undefined | Visual indicator for active state. "dot" shows grey dot normally, green when active. "check" shows green checkmark only when active. | |
| iconClassName | string | Additional className for the icon | |
| selectionMode | "checkbox" | "radio" | undefined | Selection visual: "checkbox" renders Checkbox, "radio" renders radio circle. Defaults to "checkbox" when | |
| kbdTone | "neutral" | "brand" | "danger" | null | undefined | Tone for kbd elements. Options: "neutral" (default), "brand", "danger". | |
| kbdEmphasis | "outline" | "solid" | "ghost" | "soft" | null | undefined | Emphasis for kbd elements. Options: "solid" (default), "soft", "outline", "ghost". | |
| dot | boolean | "neutral" | "brand" | "danger" | "warning" | "success" | "info" | Dot indicator in the left column (takes precedence over icon): | |
| icon | IconComponent | Icon to display in the left icon column |
SidebarMenuSubScrollArea
| Prop | Type | Default | Description |
|---|---|---|---|
| fade | boolean | false | Add a bottom gradient fade hinting at scrollable overflow. |
| maxHeight | "none" | "dropdown" | "none" | Cap the height: unconstrained, or clamped to the shared dropdown max. |
SidebarProvider
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultOpen | boolean | true | Initial open state when uncontrolled. |
| defaultWidth | number | 248 | Initial expanded width in px, typically read from the width cookie server-side so the resized width is applied before paint (no flash). |
| open | boolean | Controlled open state — pair with | |
| onOpenChange | (open: boolean) => void | Called with the next open state on toggle; required for controlled use. |
SidebarSeparator
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "strong" | "default" | "muted" | "accent" | "default" | Visual weight of the separator line. |
| showBorder | boolean | Whether to show the border line. Defaults to true. |
SidebarTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | undefined | false | Merge props and styling onto the single child element via Slot. |
| tone | "neutral" | "brand" | "danger" | null | undefined | ||
| radius | "rounded" | "square" | "full" | null | undefined | ||
| size | ButtonSizeProp | "base" | Size token; |
| dot | string | undefined | CSS colour for a status dot; empty/undefined renders no dot. | |
| loading | boolean | undefined | false | Show a spinner and disable interaction. |
| square | boolean | null | undefined | ||
| emphasis | "link" | "outline" | "solid" | "ghost" | "soft" | "transparent" | null | undefined | ||
| asInput | boolean | null | undefined | ||
| align | "center" | "end" | "start" | null | undefined | ||
| pressed | boolean | null | undefined | ||
| testId | string | ||
| suffixIcon | IconComponent | undefined | Trailing icon (Lucide or SVG component) rendered at the end. | |
| loadingLabel | string | undefined | Label shown in place of | |
| iconClassName | string | undefined | Extra classes for the leading icon. | |
| suffixIconClassName | string | undefined | Extra classes for the trailing icon. | |
| dotPlacement | "end" | "start" | undefined | "start" | Dot position relative to content. |
| expandable | boolean | undefined | false | Icon-only at rest; expands on hover/focus-visible to reveal the label
to the right of the icon. Collapsed geometry equals the matching icon-*
size, expanded equals the text button of that size — so pass a text
size ( |
| focused | boolean | undefined | Force the focus ring on — for documentation/testing only. | |
| hovered | boolean | undefined | Force the hover state on — for documentation/testing only. | |
| icon | IconComponent | Icon to display. Defaults to PanelLeftCloseIcon. |
Also exports
- const
CONTENT_COLUMN_CLASS - const
HEADER_HEIGHT - const
SIDEBAR_COOKIE_MAX_AGE - const
SIDEBAR_COOKIE_NAME - const
SIDEBAR_KEYBOARD_SHORTCUT - const
SIDEBAR_TRANSITION - const
SIDEBAR_WIDTH - const
SIDEBAR_WIDTH_COOKIE_NAME - const
SIDEBAR_WIDTH_DEFAULT_PX - const
SIDEBAR_WIDTH_ICON - const
SIDEBAR_WIDTH_MAX_PX - const
SIDEBAR_WIDTH_MIN_PX - const
SIDEBAR_WIDTH_MOBILE - const
sidebarAnatomy - component
SidebarContent - component
SidebarFooter - component
SidebarGroup - component
SidebarGroupContent - component
SidebarHeader - component
SidebarInset - component
SidebarMenu - component
SidebarMenuBadge - component
SidebarMenuItem - component
SidebarMenuSub - component
SidebarMenuSubItem - component
SidebarRail - const
useSidebar - const
useSidebarResize
Anatomy
SidebarProvider
sidebar-wrapperProvider wrapper managing sidebar state and layout
Sidebar
sidebarThe collapsible navigation panel
SidebarHeader
sidebar-headerTop region for branding or search
SidebarContent
sidebar-contentScrollable main region holding menus and groups
SidebarMenu
sidebar-menuList of navigation entries
SidebarMenuItem
sidebar-menu-itemSingle menu entry
SidebarMenuButton
sidebar-menu-buttonInteractive navigation link
SidebarFooter
sidebar-footerBottom region for account or secondary actions
SidebarTrigger
sidebar-triggerToggle that opens and closes the sidebar
SidebarInset
sidebar-insetMain content area offset beside the sidebar