An app sidebar that collapses to icons or off the canvas and becomes a sheet on phones.
Example
import {
SidebarProvider, Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarGroup,
SidebarGroupLabel, SidebarGroupContent, SidebarMenu, SidebarMenuItem, SidebarMenuButton,
SidebarRail, SidebarInset, SidebarTrigger,
} from "@elements/ui/sidebar";
import { Icon } from "@elements/ui/icon";
<SidebarProvider>
<Sidebar collapsible="icon">
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Application</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem e:for={item of items}>
<SidebarMenuButton href={item.url} isActive={item.url === path} tooltip={item.title}>
<Icon name={item.icon} /><span>{item.title}</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarRail />
</Sidebar>
<SidebarInset>
<header><SidebarTrigger /></header>
...
</SidebarInset>
</SidebarProvider>
Parts
| Part |
What it is |
SidebarProvider |
The app frame: a flex row that holds the state, the shortcut, the cookie and the phone breakpoint. Every part inside finds it. |
Sidebar |
The panel. On desktop it collapses off the canvas or to icons; under 48rem it renders its content in a Sheet. |
SidebarTrigger |
An icon button that toggles the sidebar (the sheet on phones). Place it anywhere inside the provider. |
SidebarRail |
A thin strip on the sidebar's edge that toggles it on click. Not a tab stop. |
SidebarInset |
The <main> beside the sidebar. With variant inset it becomes a raised card. |
SidebarHeader / SidebarFooter |
Sticky top and bottom sections. |
SidebarContent |
The scrolling middle. |
SidebarGroup |
A section with a label, an optional action and content. |
SidebarGroupLabel |
The group's heading; button makes it a button (for a CollapsibleTrigger). Hidden when collapsed to icons. |
SidebarGroupAction |
An icon button in the group's corner. |
SidebarGroupContent |
The group's body. |
SidebarMenu / SidebarMenuItem |
A <ul> and <li> of menu rows. |
SidebarMenuButton |
A row: a link (href) or a button, with active state, sizes, an outline variant and a tooltip shown while collapsed to icons. |
SidebarMenuAction |
An icon button at the end of a row; showOnHover hides it until the row is hovered or focused. |
SidebarMenuBadge |
A count at the end of a row. |
SidebarMenuSkeleton |
A loading row, with an optional icon. |
SidebarMenuSub / SidebarMenuSubItem / SidebarMenuSubButton |
A nested list of links under a row. |
SidebarSeparator |
A rule between sections. |
SidebarInput |
A compact search field for the header. |
Attributes
SidebarProvider
| Attribute |
Type |
Default |
Description |
open |
boolean (two-way) |
true |
Desktop: expanded or collapsed. Bind it to control it; the sidebar writes it back when the trigger, rail or shortcut toggles it. |
openMobile |
boolean (two-way) |
false |
Phone: the sheet is open. |
isMobile |
boolean (two-way) |
false |
Written by the provider: true under the breakpoint. Bind it to read it. |
shortcut |
string |
"b" |
Cmd (Mac) or Ctrl plus this key toggles the sidebar. Empty turns it off. |
cookie |
string |
"sidebar_state" |
Cookie that remembers open for a week, so the server can render it. Empty turns it off. |
cookieMaxAge |
number |
604800 |
Seconds the cookie lasts. |
mobileBreakpoint |
number |
768 |
Below this width (px) the sidebar is a sheet and an inset page drops its card. |
onOpenChange |
(open: boolean) => void |
() => {} |
The sidebar toggled itself (trigger, rail, shortcut). |
onOpenMobileChange |
(open: boolean) => void |
() => {} |
The phone sheet opened or closed itself. |
Sidebar
| Attribute |
Type |
Default |
Description |
side |
"left" | "right" |
"left" |
Which edge. |
variant |
"sidebar" | "floating" | "inset" |
"sidebar" |
Flush with a border, floating with a shadow, or beside an inset main. |
collapsible |
"offcanvas" | "icon" | "none" |
"offcanvas" |
How it collapses: off the canvas, to icons, or not at all. |
dir |
"ltr" | "rtl" | "" |
"" |
Text direction for the panel and the phone sheet; empty inherits. Pair rtl with side right for the start edge. |
label |
string |
"Sidebar" |
The navigation landmark's name, and the sheet's title on phones. |
SidebarMenuButton, SidebarMenuSubButton
| Attribute |
Type |
Default |
Description |
href |
string |
"" |
Renders a link instead of a button. |
isActive |
boolean |
false |
The current page: highlighted, aria-current. |
SidebarMenuButton
| Attribute |
Type |
Default |
Description |
variant |
"default" | "outline" |
"default" |
Plain or outlined row. |
size |
"default" | "sm" | "lg" |
"default" |
Row height. lg fits a two-line team or user row. |
tooltip |
string |
"" |
Shown beside the row while the sidebar is collapsed to icons. |
disabled |
boolean |
false |
Not interactive. |
SidebarMenuSubButton
| Attribute |
Type |
Default |
Description |
size |
"sm" | "md" |
"md" |
Text size. |
SidebarMenuAction
| Attribute |
Type |
Default |
Description |
showOnHover |
boolean |
false |
Hidden until the row is hovered or focused (desktop with a mouse). |
SidebarGroupAction, SidebarMenuAction
| Attribute |
Type |
Default |
Description |
label |
string |
(required) |
The icon button's accessible name. |
SidebarGroupLabel
| Attribute |
Type |
Default |
Description |
button |
boolean |
false |
Render a <button>, for a CollapsibleTrigger. |
SidebarMenuSkeleton
| Attribute |
Type |
Default |
Description |
showIcon |
boolean |
false |
A square before the text bar. |
SidebarInput
| Attribute |
Type |
Default |
Description |
value |
string (two-way) |
"" |
The field's text. |
SidebarTrigger, SidebarRail
| Attribute |
Type |
Default |
Description |
label |
string |
"Toggle Sidebar" |
The accessible name. |
SidebarTrigger, SidebarMenuButton, actions
| Attribute |
Type |
Default |
Description |
onclick |
(e: MouseEvent) => void |
() => {} |
Click handler. On the trigger, e.preventDefault() skips the toggle. |
most parts
| Attribute |
Type |
Default |
Description |
class, attrs |
string, Record |
"", {} |
Extra classes and attributes. |
Slots
| Slot |
Part |
Content |
default |
SidebarProvider |
Sidebar, SidebarInset and anything else in the app frame. |
default |
Sidebar |
Header, content, footer and rail. |
default |
SidebarMenuButton |
An <Icon> and a <span> label; the label truncates. |
Keyboard
| Keys |
Action |
Cmd + B / Ctrl + B |
Toggles the sidebar (the sheet on phones), except while typing in a field. |
Tab / Shift + Tab |
Moves through the trigger, menu buttons, actions and sub-items in order. |
Enter / Space |
Activates a menu button, action or trigger. |
Escape |
Closes the phone sheet and returns focus to the trigger. |
Data attributes
| Attribute |
Values |
On |
Description |
data-state |
"expanded" | "collapsed" |
.sidebar-wrapper, .sidebar |
|
data-collapsible |
"offcanvas" | "icon" | "" |
.sidebar |
The mode while collapsed, empty while expanded. |
data-mode |
"offcanvas" | "icon" | "none" |
.sidebar |
The collapsible attribute, always. |
data-side |
"left" | "right" |
.sidebar |
|
data-variant |
"sidebar" | "floating" | "inset" |
.sidebar |
|
data-mobile |
present |
.sidebar-wrapper |
Under the breakpoint. |
data-slot |
the slot names |
every part |
sidebar-wrapper, sidebar, sidebar-inner, sidebar-inset, and sidebar-<part> beside each data-sidebar part. The phone sheet's nav has data-slot and data-sidebar sidebar and data-mobile true. |
data-measured |
present |
.sidebar-wrapper |
The provider has measured mobileBreakpoint; data-mobile is current. |
data-state |
"open" | "closed" |
.sidebar-menu-button, .sidebar-menu-action |
Set by the DropdownMenu or Collapsible the row triggers; an open menu keeps its row highlighted and its action visible. |
data-active |
present |
.sidebar-menu-button, .sidebar-menu-sub-button |
|
data-size |
the size |
.sidebar-menu-button, .sidebar-menu-sub-button |
|
data-sidebar |
the part name |
every part |
The part markers: sidebar, header, content, group, menu-button, rail, trigger and the rest. |
--sidebar-width, --sidebar-width-icon, --sidebar-width-mobile |
length |
.sidebar-wrapper |
Set on the provider's class to resize. |
--sidebar, --sidebar-foreground, --sidebar-primary, --sidebar-primary-foreground, --sidebar-accent, --sidebar-accent-foreground, --sidebar-border, --sidebar-ring |
color |
.sidebar-wrapper |
The sidebar's theme variables; unset, they come from the theme contract. |
--sidebar-height |
length |
.sidebar-wrapper |
Default 100svh; set it for a sidebar inside a smaller frame. |
Accessibility
- The sidebar is a
<nav> landmark named by label; on phones it is a modal sheet (focus trapped, Escape closes, focus returns to the trigger).
- Each SidebarGroup is a group named by its SidebarGroupLabel. Collapsed off the canvas, the panel is hidden, so Tab never reaches a control the user cannot see.
- SidebarTrigger is an icon button named "Toggle Sidebar" with aria-expanded following the open state.
- A link menu button marks the current page with aria-current="page"; the rail is a mouse affordance and not a tab stop, since the trigger and the shortcut cover the keyboard.
- While collapsed to icons, every menu button keeps its name and shows it in a tooltip on hover and focus.
See also