Animated Sidebar

App navigation sidebar that folds into an icon rail.

Beacon⌘/Ctrl+B to toggle

Installation

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

Usage

Composition

Examples

Icon 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.
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.
aria-labelstring"Sidebar"Accessible name of the aside.
data-state
expanded or collapsed.
data-variant
floating or sidebar.
data-side
left or right.
Data attributeDescription
data-stateexpanded or collapsed.
data-variantfloating or sidebar.
data-sideleft or right.

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.