Animated Sidebar
App navigation sidebar that folds into an icon rail.
Installation
pnpm dlx shadcn@latest add @paneui/animated-sidebarUsage
Composition
Examples
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.
Related
See also AI Sidebar.
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.
nulldisables the shortcut.
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | — | Controlled expanded state. |
| defaultOpen | boolean | true | Initial expanded state when uncontrolled. |
| onOpenChange | (open: boolean) => void | — | Called when the sidebar expands or collapses. |
| shortcut | string | null | "b" | Key that toggles with ⌘/Ctrl. Only the most recently mounted, focused or clicked sidebar responds. null disables the shortcut. |
- data-state
expandedorcollapsed.
| Data attribute | Description |
|---|---|
| data-state | expanded 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"
floatingis a raised rounded card;sidebarsits flush with a hairline edge.- collapsible= "icon"
- "icon" | "none"
iconfolds into an icon rail;nonenever collapses.- panelClassName
- string
- Class for the inner panel surface.
- aria-label= "Sidebar"
- string
- Accessible name of the aside.
| Prop | Type | Default | Description |
|---|---|---|---|
| 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. |
| panelClassName | string | — | Class for the inner panel surface. |
| aria-label | string | "Sidebar" | Accessible name of the aside. |
- data-state
expandedorcollapsed.- data-variant
floatingorsidebar.- data-side
leftorright.
| Data attribute | Description |
|---|---|
| data-state | expanded or collapsed. |
| data-variant | floating or sidebar. |
| data-side | left or right. |
SidebarGroupLabel
Small caps heading; folds into a short rule in the icon rail. Also accepts every prop of div.
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
labelwhen 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.
| Prop | Type | Default | Description |
|---|---|---|---|
| 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 | boolean | false | 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 | boolean | false | 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. |
- data-active
- Present while the pill sits on this row (itself active, or a hidden active child).
| Data attribute | Description |
|---|---|
| data-active | Present 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
openorclosed.
| Data attribute | Description |
|---|---|
| data-state | open 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.
| Prop | Type | Default | Description |
|---|---|---|---|
| isActive | boolean | false | 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. |
- data-active
- Present when active.
| Data attribute | Description |
|---|---|
| data-active | Present when active. |
SidebarTrigger
Round button that toggles the sidebar. Shows SidebarIcon unless you pass children. Also accepts every prop of button.
- data-state
expandedorcollapsed.
| Data attribute | Description |
|---|---|
| data-state | expanded or collapsed. |
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.
| Prop | Type | Default | Description |
|---|---|---|---|
| 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. |