Animated Sidebar

App navigation sidebar that folds into an icon rail.

Beacon

Installation

pnpm dlx shadcn@latest add @paneui/animated-sidebar

Usage

Composition

Examples

Collapsed

Three Columns

Horizontal Rail

Flush, Right Side

Controlled

Non-collapsible Links

RTL

Keyboard

⌘/Ctrl + B toggles the sidebar; change the key with shortcut or pass null to turn it off. All sidebars share one listener, so with several on a page, only the most recently mounted, focused or clicked one toggles. Disclosure items set aria-expanded and aria-controls.

See also AI Sidebar.

API Reference

SidebarProvider

Holds the open state and lays out the sidebar and inset in a row. Also accepts every prop of div.

open
boolean
Controlled expanded state.
defaultOpen= true
boolean
Initial expanded state when uncontrolled.
onOpenChange
(open: boolean) => void
Called when the sidebar expands or collapses.
shortcut= "b"
string | null
Key that toggles with ⌘/Ctrl. Only the most recently mounted, focused or clicked sidebar responds. null disables the shortcut.
PropTypeDefaultDescription
openboolean—Controlled expanded state.
defaultOpenbooleantrueInitial expanded state when uncontrolled.
onOpenChange(open: boolean) => void—Called when the sidebar expands or collapses.
shortcutstring | null"b"Key that toggles with ⌘/Ctrl. Only the most recently mounted, focused or clicked sidebar responds. null disables the shortcut.
data-state
expanded or collapsed.
Data attributeDescription
data-stateexpanded or collapsed.

Sidebar

The panel. Its width springs between --sidebar-width (15rem) and --sidebar-width-icon (3.75rem). Also accepts every prop of motion.aside.

side= "left"
"left" | "right"
Which side of the inset it sits on.
variant= "floating"
"floating" | "sidebar"
floating is a raised rounded card; sidebar sits flush with a hairline edge.
collapsible= "icon"
"icon" | "none"
icon folds into an icon rail; none never collapses.
panelClassName
string
Class for the inner panel surface.
rail
ReactNode
A SidebarRail shown beside the sidebar's content: rail, second level, then main. Collapsing hides the second level and keeps the rail.
aria-label= "Sidebar"
string
Accessible name of the aside.
PropTypeDefaultDescription
side"left" | "right""left"Which side of the inset it sits on.
variant"floating" | "sidebar""floating"floating is a raised rounded card; sidebar sits flush with a hairline edge.
collapsible"icon" | "none""icon"icon folds into an icon rail; none never collapses.
panelClassNamestring—Class for the inner panel surface.
railReactNode—A SidebarRail shown beside the sidebar's content: rail, second level, then main. Collapsing hides the second level and keeps the rail.
aria-labelstring"Sidebar"Accessible name of the aside.
data-state
expanded or collapsed.
data-variant
floating or sidebar.
data-side
left or right.
data-rail
Present in the three-column layout.
Data attributeDescription
data-stateexpanded or collapsed.
data-variantfloating or sidebar.
data-sideleft or right.
data-railPresent in the three-column layout.

SidebarHeader

Also accepts every prop of div.

SidebarContent

Scrolling middle section. Also accepts every prop of div.

SidebarFooter

Bottom section with a hairline divider. Also accepts every prop of div.

SidebarGroup

Also accepts every prop of div.

SidebarGroupLabel

Small caps heading; folds into a short rule in the icon rail. Also accepts every prop of div.

SidebarMenu

Also accepts every prop of ul.

SidebarItem

A row. With a SidebarSub child it becomes a disclosure. Also accepts every prop of button.

label*
ReactNode
Row text; fades out in the rail.
icon
ReactNode
Leading icon, 16px.
tooltip
string
Tooltip in the icon rail. Defaults to label when it is a string.
badge
ReactNode
Trailing count pill.
isActive= false
boolean
Show the active pill and set aria-current="page".
href
string
Render as a link.
disabled
boolean
Dim and ignore clicks.
open
boolean
Controlled disclosure state.
defaultOpen= false
boolean
Initial disclosure state.
onOpenChange
(open: boolean) => void
Called when the disclosure opens or closes.
render
ReactElement | (props, state) => ReactElement
Replace the rendered element, e.g. a router Link.
itemClassName
string
Class for the wrapping li.
PropTypeDefaultDescription
label*ReactNode—Row text; fades out in the rail.
iconReactNode—Leading icon, 16px.
tooltipstring—Tooltip in the icon rail. Defaults to label when it is a string.
badgeReactNode—Trailing count pill.
isActivebooleanfalseShow the active pill and set aria-current="page".
hrefstring—Render as a link.
disabledboolean—Dim and ignore clicks.
openboolean—Controlled disclosure state.
defaultOpenbooleanfalseInitial disclosure state.
onOpenChange(open: boolean) => void—Called when the disclosure opens or closes.
renderReactElement | (props, state) => ReactElement—Replace the rendered element, e.g. a router Link.
itemClassNamestring—Class for the wrapping li.
data-active
Present while the pill sits on this row (itself active, or a hidden active child).
Data attributeDescription
data-activePresent while the pill sits on this row (itself active, or a hidden active child).

SidebarSub

Nested list of a disclosure item; folds away in the rail. Also accepts every prop of motion.ul.

data-state
open or closed.
Data attributeDescription
data-stateopen or closed.

SidebarSubItem

Also accepts every prop of button.

isActive= false
boolean
Show the active pill.
href
string
Render as a link.
disabled
boolean
Dim and ignore clicks.
icon
ReactNode
Leading icon.
render
ReactElement | (props, state) => ReactElement
Replace the rendered element.
PropTypeDefaultDescription
isActivebooleanfalseShow the active pill.
hrefstring—Render as a link.
disabledboolean—Dim and ignore clicks.
iconReactNode—Leading icon.
renderReactElement | (props, state) => ReactElement—Replace the rendered element.
data-active
Present when active.
Data attributeDescription
data-activePresent when active.

SidebarTrigger

Round button that toggles the sidebar. Shows SidebarIcon unless you pass children. Also accepts every prop of button.

data-state
expanded or collapsed.
Data attributeDescription
data-stateexpanded or collapsed.

SidebarInset

The main content area beside the sidebar. Also accepts every prop of main.

SidebarHideCollapsed

Wrapper for header or footer content that hides in the icon rail. Also accepts every prop of div.

useSidebar

Read the sidebar state inside SidebarProvider.

open
boolean
Whether it is expanded.
setOpen
(open: boolean) => void
Set the state.
toggle
() => void
Flip the state.
state
"expanded" | "collapsed"
Same as open, as a string.
reduce
boolean
Whether reduced motion is on.
pillId
string
Shared layout id of the active pill.
PropTypeDefaultDescription
openboolean—Whether it is expanded.
setOpen(open: boolean) => void—Set the state.
toggle() => void—Flip the state.
state"expanded" | "collapsed"—Same as open, as a string.
reduceboolean—Whether reduced motion is on.
pillIdstring—Shared layout id of the active pill.

SidebarRail

Also accepts every prop of nav.

orientation= "vertical"
"vertical" | "horizontal"
A 48px column, or a 48px row (groups, separators and the spacer follow).
tooltipSide= "right" / "top"
"right" | "left" | "top" | "bottom"
Side tooltips open on. Defaults to right for a vertical rail and top for a horizontal one; use left for a rail on the right edge.
PropTypeDefaultDescription
orientation"vertical" | "horizontal""vertical"A 48px column, or a 48px row (groups, separators and the spacer follow).
tooltipSide"right" | "left" | "top" | "bottom""right" / "top"Side tooltips open on. Defaults to right for a vertical rail and top for a horizontal one; use left for a rail on the right edge.
data-orientation
vertical or horizontal.
Data attributeDescription
data-orientationvertical or horizontal.

SidebarRailItem

Also accepts every prop of button.

label*
string
Accessible name and tooltip text.
icon*
ReactNode
The glyph.
shortcut
string
Shown as a key chip in the tooltip.
active= false
boolean
Selected state; sets aria-current="page".
badge
number | boolean
Count or dot.
render
ReactElement
Render as another element, e.g. a Link.
PropTypeDefaultDescription
label*string—Accessible name and tooltip text.
icon*ReactNode—The glyph.
shortcutstring—Shown as a key chip in the tooltip.
activebooleanfalseSelected state; sets aria-current="page".
badgenumber | boolean—Count or dot.
renderReactElement—Render as another element, e.g. a Link.
data-active
Present when active.
Data attributeDescription
data-activePresent when active.

SidebarRailAvatar

Round button for accounts or agent providers at the bottom of the rail. Also accepts every prop of button.

label*
string
Accessible name and tooltip.
status= "offline"
"online" | "busy" | "error" | "offline"
Ring colour. online is success, busy warning, error destructive and offline has none.
hint
string
Extra tooltip text, e.g. the connection state.
PropTypeDefaultDescription
label*string—Accessible name and tooltip.
status"online" | "busy" | "error" | "offline""offline"Ring colour. online is success, busy warning, error destructive and offline has none.
hintstring—Extra tooltip text, e.g. the connection state.

SidebarRailGroup

Also accepts every prop of div.

SidebarRailSeparator

Also accepts every prop of div.

SidebarRailSpacer

Pushes what follows to the bottom. Also accepts every prop of div.