Sidebar
ProThe application navigation rail. Compose switchers, sections and rows that carry state, then pick the shape: a list, a labeled icon rail, a rail with panels, a peeking rail or a page tree.
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
Sidebar is a flex column on a bg-card surface. SidebarHeader and SidebarFooter pin to the top and bottom with a hairline separator, and either can hold a SidebarActions cluster beside its switcher; SidebarContent is the scroll region in between. Inside it, SidebarGroup is a navigation section. Give it a SidebarGroupLabel when it needs a heading, or leave it unlabeled for the primary pages. Each SidebarItem is a row; wrap it in a SidebarItemRow when it carries its own actions. The rail and panel, peek, rich row and page tree parts compose around this same skeleton and are listed in the API reference.
import {
Sidebar,
SidebarHeader,
SidebarSwitcher,
SidebarActions,
SidebarContent,
SidebarGroup,
SidebarGroupLabel,
SidebarGroupAction,
SidebarGroupContent,
SidebarItemRow,
SidebarItem,
SidebarItemIcon,
SidebarItemDot,
SidebarItemCount,
SidebarItemActions,
SidebarItemAction,
SidebarCollapsible,
SidebarSeparator,
SidebarFooter,
} from "@/components/ui/sidebar"
<Sidebar>
<SidebarHeader>
<SidebarSwitcher />
<SidebarActions>...</SidebarActions>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel />
<SidebarGroupAction />
<SidebarGroupContent>
<SidebarItemRow>
<SidebarItem>
<SidebarItemIcon /> {/* or <SidebarItemDot /> */}
<SidebarItemCount />
</SidebarItem>
<SidebarItemActions>
<SidebarItemAction />
</SidebarItemActions>
</SidebarItemRow>
<SidebarCollapsible>
<SidebarItem />
</SidebarCollapsible>
</SidebarGroupContent>
</SidebarGroup>
<SidebarSeparator />
</SidebarContent>
<SidebarFooter>
<SidebarSwitcher />
</SidebarFooter>
</Sidebar>Switchers#
SidebarSwitcher is one trigger used for both the workspace switcher (top) and the profile switcher (bottom): a leading visual, a title (with an optional subtitle), and an up/down caret. It renders a real <button>, so it drops straight into a DropdownMenuTrigger asChild. Radix owns the keyboard, focus, and ARIA; the switcher stays lit while its menu is open.
Full switcher#
SidebarSwitcher takes a variant. The default is the compact trigger (a tight gap, a small muted caret, and reduced padding), the everyday switcher, paired with a small leading (an Avatar size="xs", a single initial, or a compact logo tile). Pass variant="full" for the roomier identity row: a wider gap, a heavier title, and room for a subtitle. Pair it with a larger leading (an Avatar size="md", which keeps both initials) for a primary or standalone switcher.
Header actions#
SidebarActions puts icon buttons beside a switcher: search and notifications next to the workspace, the way GitBook and Squarespace head their rail. Drop it into SidebarHeader (or SidebarFooter, for help or settings) after the switcher, and the zone lays the two out as a row: the switcher hugs its name so the caret follows it, and the icons sit at the far edge, in line with the ends of the rows below. Fill it with ghost icon-only Buttons; they take the rail’s muted ink at rest and their aria-label as a tooltip.
This is where an app’s global tools live in the docked shell, which leaves the sheet’s header bar for the page. Collapsed to the icon rail, the buttons stack under the switcher’s mark instead of being cut off.
Items#
A SidebarItem is a <button> by default; pass asChild to wrap an <a> or a Next <Link>. Mark the current page with active (it fills the row and sets aria-current="page"). Lay the children out as <Icon /> Label and push a trailing Badge or count to the right with ml-auto.
Colored icon tiles#
Swap the bare leading glyph for a SidebarItemIcon: a soft, hue-tinted rounded square that gives each destination its own color, the “colored spaces” rail you see in Productboard or Linear. Wrap a single icon and pass a tone (decorative: purple, pink, teal, orange; status: green, blue, red, amber, plus neutral, the default, and brand). The tile scales with density and becomes the centered chip in the collapsed rail. The active row still reads through the neutral chip and ink bar, the tiles decorate, they don’t signal selection.
Nested navigation#
SidebarCollapsible folds a sub-list of routes under a parent row. Give it an icon and label, drop the child SidebarItems inside, and the row gains a caret that toggles them open, built on Radix Collapsible, so it owns the keyboard, focus, and height animation. The sub-list is indented under the label and traced by a guide rail; set active on the parent when one of its child routes is current. In the collapsed icon rail it falls back to a single icon row.
A long sub-list can be split under SidebarGroupLabel sub-headings: inside a sub-list the label stays a plain heading, even when the whole section sits in a collapsible group. With indicator on, folding a branch that holds the current route moves the pill to the first row still on show, so a parent marked active while folded catches it. The docs sidebar on this site is built exactly this way.
Collapsible sections#
Pass collapsible to a SidebarGroup and its SidebarGroupLabel becomes a toggle with a caret. Wrap the rows in SidebarGroupContent so they fold beneath it. Use defaultOpen (or the controlled open/onOpenChange pair) to set the initial state. Unlike nested navigation, this folds a whole flat section rather than revealing sub-routes.
Row states#
Rows carry state, not just selection: unread brings a row to full ink and semibold, muted sinks it, and SidebarItemCount adds a quiet tabular count. Wrap a row in SidebarItemRow to give it hover-revealed SidebarItemActions, and add a SidebarGroupAction after a heading for a section +. In the collapsed rail unread becomes a small ink dot on the glyph, and counts and actions step aside.
A plain count takes the row’s own ink, so a column of totals reads as one quiet figure per row and lifts only where the row is active or unread. Reach for a Badge when the number is a label rather than a count. For a colour where the icon goes (a mail label, a project), use SidebarItemDot: it fills the same 20px glyph column, so dotted and iconed rows line up (the Mail recipe below uses it for labels).
Collapsed (icon rail)#
The rail has two variants, driven by the same markup. The default is the full-width rail; collapsed shrinks it to a 48px icon-only column. Labels go sr-only, each row becomes a centered icon, the active row is marked with a left ink bar instead of a full chip, sections are divided by a short rule, and the switcher reduces to its leading visual. Give every SidebarItem a label so the collapsed row gets a hover tooltip and an accessible name, and mark secondary sections with <SidebarGroup hideWhenCollapsed> so they drop out of the rail rather than reducing to bare glyphs.
Stacked (labeled icon rail)#
layout="stacked" is the rail Slack and Teams lead with: a 72px column where every row sets its glyph in a rounded tile and keeps a short label underneath. The tile is the whole marker, lit on hover and held while active, and a Badge in the row pins to its corner as the unread count. Like the collapsed rail, headings go sr-only, sections split on a short rule and the switchers reduce to their mark. Set it beside an ordinary rail for the two-pane workspace shell: the stacked rail picks the area, the list rail carries its channels.
Bare stacked#
The stacked rail lights a rounded tile behind the glyph on hover and selection, the Slack read. Pass tile={false} for the bare rail of Teams: no tile at all, the current row carries the ink edge bar and its glyph and label turn full ink. The glyph keeps its padded box either way, so badges and unread dots pin to the same corner.
Rail and panel#
SidebarPanels pairs a narrow icon rail with a 240px panel for the area it selects, as in Supabase, Discord or the VS Code activity bar. The rail is a keyboard-navigable tablist, and each SidebarPanel is an ordinary list Sidebar with its own title and groups. Clicking the open area again folds the panel away (collapseOnReselect); turn that off for a console where the panel is always out.
With collapseOnReselect on (the default) the rail behaves like the VS Code activity bar: click the open view and the panel folds, leaving no lit row and the whole width to the content. Opening and closing animate the panel’s width; switching areas while open is an instant swap. Drive it from anywhere inside with useSidebarPanels().
Peek#
SidebarPeek shows a rail on demand. mode="expand" keeps a 48px icon rail that widens over the page after a short hover or keyboard focus, so the content never moves. Pin it with SidebarPeekPin to dock it; the peek closes on leave, Escape or focus out, and holds while a menu inside it is open.
Two trade-offs are deliberate. While it peeks, the rail keeps its icon-rail form, so headings stay short rules and hideWhenCollapsed groups and nested sub-lists only appear once it is pinned. And pinning reflows the page on purpose: it is the one change that should move the content, so the column tweens to the full width.
mode="offcanvas" hides the rail entirely and slides it in as a floating card from an 8px hot zone on the left edge or from a SidebarPeekTrigger in the content. Resting on the trigger peeks; clicking it docks the rail, and clicking again sends it away. SidebarPeek renders no DOM, so the trigger can live in any top bar.
Rich rows#
SidebarRichItem is the same SidebarItem one size up, for rails whose rows are records: leading media, a title with a timestamp, a one-line preview and an unread count. Collapsed, only the avatars or covers remain, with a dot marking unread. The text block is a fixed two-by-two grid, so any part can be left out.
size="lg" is the library row: a 48px cover, square or round by shape, with a SidebarItemMeta placement="center" for a now-playing glyph. SidebarHeaderRow, SidebarTitle and SidebarFilters stack a titled header, a scrolling chip row and a search row in one SidebarHeader.
Page tree#
SidebarTree nests pages to any depth with a 12px step per level. On hover or focus a page’s icon turns into its caret, and its actions (a … menu, a +) appear at the row’s end. Rows match SidebarItem exactly, the keyboard model is the one Tree uses, and clicking a row navigates while only the caret folds.
In the collapsed rail the tree shrinks to its top-level pages: each one a glyph with its label as a tooltip, lit while the open page sits anywhere beneath it. A section whose pages mean nothing as bare glyphs should pass hideWhenCollapsed to its SidebarGroup instead, as Favorites does here.
Docked (in an app shell)#
A rail standing on its own is a card: it paints bg-card and a hairline against the content beside it. Inside an app shell it shouldn’t: the shell already owns the surface, and a second fill on top of it is the difference between a docked dashboard and a rail bolted onto one. So nesting a Sidebar in a LayoutSidebar turns docked on for you: no fill, no hairline, and the width comes from the shell column, which is also what animates the collapse.
The same applies in LayoutMobileSidebar, where the drawer panel is the surface. Pass docked explicitly to force the treatment outside a Layout, or docked={false} to opt a rail out of the shell it sits in. Combined with floating below, the card treatment wins: that pair is the detached rail floating in the shell gutter.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Floating (inset)#
floating detaches the rail into an inset card: a full outline, a rounded radius concentric with the app shell, and a shadow that lifts it off the canvas, instead of a single edge hairline. Pair it with padding on the container (or the Layout gutter) so it floats clear of the viewport edges.
Right side#
side="right" docks the rail against the right edge. The separating hairline flips to its left so it reads as an inspector/detail panel beside the main content. Everything else (switchers, sections, density) is symmetric.
Density#
density on Sidebar flows through context to the zone padding, the inter-section gap, the row height, and the switcher padding. compact is the default (app UI); comfortable is roomier. Set it once here or for a whole subtree with DensityProvider. See Density.
Recipes#
Five finished rails built from the parts above, nothing bespoke: each one is plain composition of rows, states, counts and the footer. Lift one whole into an app, or use it as the reference for how the parts combine.
Teams, as in Linear: each team folds its own views under a tinted tile.
Mail: one Compose action, folders with a column of totals, labels as colour dots.
Usage quota: the plan’s meter and its upgrade sit at the foot, above the profile.
Presence: direct messages lead with an avatar carrying its status dot.
User card, as in Discord: the profile, then mute, deafen and settings at one size.
API reference#
Sidebar
The rail: a 256px <aside> landmark (name it with aria-label) that hands density and the collapsed state to every part. Forwards all native <aside> props, and every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| collapsed | boolean | false | Shrinks the rail to a 48px icon-only column. Inherited from an enclosing LayoutSidebar; an explicit value wins. |
| layout | "list" | "stacked" | "list" | stacked is the labeled icon rail of Slack or Teams: a 72px column, each glyph in a rounded tile above a short label, a Badge pinned to the tile's corner. |
| tile | boolean | true | The stacked rail's selection marker. On, hover and the current row light a rounded tile behind the glyph (Slack); off, the rail goes bare (Teams) and the current row carries the ink edge bar. Only read by layout="stacked". |
| indicator | boolean | false | One highlight that glides to the active row instead of a static chip per row. Ignored while collapsed. |
| side | "left" | "right" | "left" | The edge it docks against; the hairline moves to the inner edge. |
| floating | boolean | false | Detaches the rail into a rounded card with a full outline and shadow. |
| docked | boolean | false | Sits the rail on the app canvas: no fill, no hairline, full width of its column. Inherited from an enclosing LayoutSidebar. |
| density | "compact" | "comfortable" | "compact" | Zone padding, row height and switcher padding. Falls back to the nearest DensityProvider. |
| Data attribute | Description |
|---|---|
| data-collapsed | Present while the rail is collapsed. |
| data-layout | list or stacked. |
| data-docked | Present while the rail is docked. |
| CSS variable | Description |
|---|---|
| --surface | Set to --card (reset when docked) so nested inputs paint the rail's surface. |
SidebarHeader
The top zone, with a hairline below, for the workspace switcher; holding a SidebarActions turns it into a row. Forwards all native <div> props.
SidebarContent
The scroll region between header and footer; it measures the active row for the indicator pill. Forwards all native <div> props.
SidebarFooter
The bottom zone, with a hairline above, for the profile switcher; holding a SidebarActions turns it into a row. Forwards all native <div> props.
SidebarActions
A cluster of ghost icon buttons beside the switcher, pushed to the zone's far edge; it stacks under the mark when collapsed. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "xl" | "md" | The height every control in the cluster takes, so buttons and toggles land on one size instead of each control's own default. |
| Data attribute | Description |
|---|---|
| data-control-size | The resolved size. |
SidebarGroup
A section rendered as a <nav> landmark; name it with aria-label. Forwards all native <nav> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| hideWhenCollapsed | boolean | false | Drops the section entirely while the rail is collapsed. |
| collapsible | boolean | false | Turns SidebarGroupLabel into a Radix Collapsible trigger that folds SidebarGroupContent. Off while the rail is collapsed. |
| defaultOpen | boolean | true | Starts expanded (uncontrolled); only with collapsible. |
| open | boolean | - | Open state (controlled); only with collapsible. |
| onOpenChange | (open: boolean) => void | - | Called when a collapsible group toggles. |
SidebarGroupLabel
The section heading: a muted <div>, or in a collapsible group a full-width <button> with a caret that turns on open. Screen-reader only while collapsed.
| Data attribute | Description |
|---|---|
| data-state | open or closed, on the button of a collapsible group. |
SidebarGroupContent
The rows of a section. In a collapsible group it is the Radix Collapsible panel; otherwise an optional plain stack. Forwards all native <div> props.
| Data attribute | Description |
|---|---|
| data-state | open or closed, in a collapsible group. |
SidebarGroupAction
The button at the end of a section heading (Slack's "Channels +"). Place it right after SidebarGroupLabel; it rests hidden and appears while the pointer or keyboard focus is in the section, and sits before the caret of a collapsible group. Dropped in an icon rail. Forwards all native <button> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | string | - | The accessible name of the icon-only button ("Add channel"). |
| Data attribute | Description |
|---|---|
| data-state | open while a menu or dialog it triggers is open; keeps it on show. |
SidebarItem
A nav row, a <button> by default; lay its children out as <Icon /> Label with an optional trailing count. Collapsed, it keeps only the leading glyph. Forwards all native <button> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Renders the child element (an <a> or Next <Link>) as the row, through Radix Slot. |
| active | boolean | false | Marks the current page: fills the row, adds the ink bar and sets aria-current="page". |
| label | string | - | The collapsed rail's hover tooltip and accessible name for the row. |
| unread | boolean | false | Something new is waiting: the label goes full ink and semibold without the active chip. In an icon rail a small ink dot marks the glyph's corner instead. Announced as "unread". |
| muted | boolean | false | A silenced row: it sinks below the rail's muted ink until hovered or current. Wins over unread. |
| Data attribute | Description |
|---|---|
| data-active | true on the active row. |
| data-unread | Present on an unread row (not when it is also muted). |
| data-muted | Present on a muted row. |
SidebarItemCount
A trailing count, dropped last in a SidebarItem: right-aligned tabular figures in the row's own ink. Gone in an icon rail, and inside a SidebarItemRow it gives its place to the actions while they show. Forwards all native <span> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "plain" | "pill" | "plain" | plain is the quiet total; pill the unread capsule of a conversation row. |
| tone | "neutral" | "brand" | "muted" | "neutral" | The pill's fill: neutral (ink) for a thread that wants attention, brand for the accent, muted for a muted one. Ignored by plain. |
| srLabel | string | "unread" for pill, none for plain | Read after the number by screen readers, so 3 is announced as "3 unread". |
| Data attribute | Description |
|---|---|
| data-variant | plain or pill. |
SidebarItemRow
Wraps a SidebarItem and its SidebarItemActions side by side (a button can't hold another button) and drives their reveal from its hover and keyboard focus. In an icon rail it renders the row alone. Forwards all native <div> props.
SidebarItemActions
The trailing cluster of a SidebarItemRow, laid over the row's end. Hidden at rest; shown on row hover, keyboard focus in the row, or while a menu it opened is open. Dropped in an icon rail. Forwards all native <div> props.
SidebarItemAction
One small ghost icon button in a SidebarItemActions cluster (a … menu, a +), with a 40px tall hit area. A real <button>, so it drops into DropdownMenuTrigger asChild. Forwards all native <button> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | string | - | The accessible name of the icon-only button ("Channel options"). |
| Data attribute | Description |
|---|---|
| data-state | open while its menu is open; keeps it lit. |
SidebarItemIcon
A soft hue-tinted tile around a single icon, sized by density; decorative (aria-hidden). Forwards all native <span> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| tone | "neutral" | "brand" | "purple" | "pink" | "teal" | "orange" | "green" | "blue" | "red" | "amber" | "neutral" | The tint and glyph colour. |
SidebarItemDot
A colour dot where a row's icon goes (a mail label, a favourite, a project), filling the same 20px glyph column so dotted and iconed rows line up. Decorative (aria-hidden). Forwards all native <span> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| tone | "neutral" | "brand" | "purple" | "pink" | "teal" | "orange" | "green" | "blue" | "red" | "amber" | "neutral" | The dot's hue. |
SidebarCollapsible
A parent row that folds a nested list of SidebarItems beneath it, on Radix Collapsible; collapsed, it falls back to a single icon row. Takes only the props below, no native props.
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | React.ReactNode | - | The parent row's label; a string also becomes the collapsed tooltip. |
| children* | React.ReactNode | - | The nested rows; their edge bar is suppressed. |
| icon | React.ReactNode | - | Leading glyph of the parent row. |
| actions | React.ReactNode | - | Trailing content before the caret, such as a count Badge. |
| active | boolean | false | Lights the parent row when one of its child routes is current. |
| defaultOpen | boolean | false | Starts expanded (uncontrolled). |
| open | boolean | - | Open state (controlled). |
| onOpenChange | (open: boolean) => void | - | Called when the section toggles. |
| disabled | boolean | false | Disables the parent row and its toggle. |
| className | string | - | Classes for the parent row. |
| Data attribute | Description |
|---|---|
| data-state | open or closed, on the parent row and the nested list. |
SidebarSwitcher
The identity row for the workspace or profile switcher, a real <button> that drops into DropdownMenuTrigger asChild and stays lit while the menu is open. Forwards all native <button> props except title.
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | React.ReactNode | - | Primary line: the workspace or profile name. |
| subtitle | React.ReactNode | - | Secondary line: plan, email or role. |
| leading | React.ReactNode | - | Leading visual: an Avatar, a logo tile or an icon. |
| caret | boolean | true | Shows the trailing up and down caret. |
| variant | "default" | "full" | "default" | default is the compact trigger; full the roomier identity row with a semibold title. |
SidebarSeparator
A hairline role="separator" between groups. Forwards all native <div> props.
SidebarRichItem
A two-line SidebarItem one size up, for rows that are records (a conversation, a playlist). It is a SidebarItem underneath, so it takes active, label and asChild and every native <button> prop. Put SidebarItemMedia first: the collapsed rail keeps only it.
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "md" | "lg" | "md" | md is the conversation row (40px avatar, 64px rows); lg the library row (48px cover, 60px rows). |
| unread | boolean | false | The title goes semibold, and in the collapsed rail an ink dot marks the media's corner. Pair it with a SidebarItemCount for the number. |
| Data attribute | Description |
|---|---|
| data-unread | Present on an unread row. |
| data-size | md or lg. |
SidebarItemMedia
The leading avatar, cover or glyph; a dropped-in Avatar or <img> fills the square. Forwards all native <span> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| shape | "circle" | "square" | "circle" | circle for people and artists, square (8px corners) for covers, groups and channels. |
SidebarItemText
The two-line text block: a fixed two-by-two grid, so its parts can come in any order and any can be left out. Forwards all native <span> props.
SidebarItemTitle
The first line: full ink, one line with an ellipsis, semibold on an unread row. Forwards all native <span> props.
SidebarItemDescription
The second line: a preview or subtitle, muted, one line. Forwards all native <span> props.
SidebarItemMeta
Trailing detail on the right edge: a timestamp, read receipts, a now-playing glyph. Muted and tabular. Forwards all native <span> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| placement | "top" | "bottom" | "center" | "top" | Beside the title, beside the description, or centred across both lines. |
SidebarHeaderRow
One row inside a SidebarHeader that stacks several (the title with its actions, a search and sort row). Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| hideWhenCollapsed | boolean | false | Drops the row in the collapsed rail. |
SidebarTitle
The rail's heading ("Your library"), a 16px semibold <h2>, kept for screen readers when collapsed. Forwards all native <h2> props.
SidebarFilters
A horizontally scrolling chip row under the title, with a fade at its edges. Fill it with a ToggleGroup type="single" size="sm". Hidden when collapsed. Forwards all native <div> props.
SidebarPanels
The rail and panel sidebar: owns the selected area and whether its panel is out, and lays the rail and panels side by side. Forwards all native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | The selected area (controlled). |
| defaultValue | string | - | The area selected on mount (uncontrolled). Set it, so the rail has a tab stop. |
| onValueChange | (value: string) => void | - | Called when another area is picked. |
| open | boolean | - | Whether the panel is on show (controlled). |
| defaultOpen | boolean | true | Whether the panel starts on show (uncontrolled). |
| onOpenChange | (open: boolean) => void | - | Called when the panel opens or closes. |
| collapseOnReselect | boolean | true | Clicking the open area again folds the panel (VS Code). Turn it off for a panel that is always out (Supabase). |
| Data attribute | Description |
|---|---|
| data-state | open or closed: whether a panel is out. |
SidebarPanelsList
The rail's vertical tablist, in place of a SidebarGroup inside the rail's SidebarContent; name it with aria-label. Up and Down move focus (wrapping), Home and End jump; Enter, Space or a click opens. Forwards all native <div> props.
SidebarPanelsTrigger
A rail row that opens an area's panel: a SidebarItem wired as a tab, lit only while its panel is out. Takes every SidebarItem prop except active.
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | The value of the SidebarPanel it opens. |
| Data attribute | Description |
|---|---|
| data-state | open while its panel is out, else closed. |
| data-active | Present while its panel is out. |
SidebarPanel
One area's panel: a 240px list Sidebar, so the usual parts drop straight in. Takes every Sidebar prop except collapsed and layout.
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | The value of its SidebarPanelsTrigger. |
| Data attribute | Description |
|---|---|
| data-state | open while this panel is on show, else closed. |
SidebarPanelHeader
The panel's header, sized to the rail's so the two hairlines meet as one line. Forwards all native <div> props.
SidebarPanelTitle
The area's name, a 16px semibold <h2>. Forwards all native <h2> props.
useSidebarPanels
Reads and drives the panels from anywhere inside SidebarPanels (a hide button, a shortcut): returns { value, open, select, setOpen, toggle }.
SidebarPeek
The state owner for a rail that shows on demand. Renders no DOM, so a trigger anywhere in the content can reach the panel. Opens on a resting mouse or keyboard focus (never touch); closes on leave, Escape or focus out, and holds while a menu inside is open.
| Prop | Type | Default | Description |
|---|---|---|---|
| mode | "expand" | "offcanvas" | "expand" | expand: a 48px icon rail that widens over the content. offcanvas: a hidden rail that slides in as a floating card. |
| open | boolean | - | Whether the peek is showing (controlled). |
| defaultOpen | boolean | false | Start with the peek showing (uncontrolled). |
| onOpenChange | (open: boolean) => void | - | Called when the peek shows or hides. |
| pinned | boolean | - | Docked in the flow (controlled). Inside a Layout, bridge it to the shell's collapsed state so the shortcut and the pin agree. |
| defaultPinned | boolean | false | Start docked (uncontrolled). |
| onPinnedChange | (pinned: boolean) => void | - | Called when the pin or the trigger docks or undocks the rail. |
| openDelay | number | 160 | How long the pointer rests before the peek shows, in ms. |
| closeDelay | number | 160 (expand), 300 (offcanvas) | The grace after the pointer leaves before the peek hides, in ms. |
SidebarPeekPanel
The column that holds the rail: a placeholder in the flow plus the floating panel over it. Put a normal Sidebar inside as its only child; in expand mode it stays collapsed until pinned. Forwards all native <div> props.
| Data attribute | Description |
|---|---|
| data-mode | expand or offcanvas. |
| data-state | rest, peek or pinned. |
SidebarPeekPin
A ghost icon button for the rail's SidebarActions that docks or undocks it, with aria-pressed. Takes every Button prop except children and iconOnly.
SidebarPeekTrigger
The control in the content that reaches an offcanvas rail: resting on or focusing it peeks, clicking docks, clicking again sends it away. Pass children to replace the glyph. Takes every Button prop except iconOnly.
useSidebarPeek
The peek state for a bespoke control: { mode, open, setOpen, pinned, togglePinned }.
SidebarTree
A role="tree" page tree inside a SidebarGroup that owns the current page, the expansion state and the keyboard (Tree's model). Name it with aria-label. Forwards all native <ul> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | The current page (controlled): its row takes the active chip and aria-current. |
| defaultValue | string | - | The initial current page (uncontrolled). |
| onValueChange | (value: string) => void | - | Called with the page the user opened (click, Enter or Space on its row). |
| expandedValues | string[] | - | Expanded page values (controlled). |
| defaultExpandedValues | string[] | - | Initially expanded page values (uncontrolled). |
| onExpandedChange | (values: string[]) => void | - | Called with the full set of expanded values when one folds or unfolds. |
| linkAs | React.ElementType | "a" | What a page with an href renders as: pass Next's Link for client routing. |
SidebarTreeItem
A page, recursive: nest SidebarTreeItems to give it subpages and a caret. Activating the row navigates; the caret, or Right and Left, folds. Forwards native <div> props to the row, className included.
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | Stable identity for navigation and expansion. |
| label* | React.ReactNode | - | The page title; a string also names the glyph in the collapsed rail. |
| icon | React.ReactNode | - | The page's emoji or glyph. Falls back to a page glyph, so every row keeps its column. |
| href | string | - | Makes the row a real link, rendered with the tree's linkAs. |
| actions | React.ReactNode | - | Trailing SidebarItemActions, revealed on hover or keyboard focus. |
| disabled | boolean | false | Disables navigation, folding and focus for this page. |
| Data attribute | Description |
|---|---|
| data-level | The page's depth, from 0. |
| data-expanded | true or false. |
| data-active | Present on the current page. |