Sidebar
The navigation column of an application, collapsible on a wide screen and a sheet on a small one, with groups, menus, badges and submenus.
The sidebar is the navigation column of an application: a header, groups of menu items, and a
footer, beside the page. On a wide screen it can collapse out of the way or to icons; below the
md width it becomes a sheet that the reader opens with a trigger.
The navigation of this site is a sidebar. The example shows its parts in a fixed frame.
// apps/docs/examples/ui/sidebar.tsx'use client';
import {useState} from 'react';import { FileTextIcon, HomeIcon, PlusIcon, SettingsIcon, UsersIcon,} from 'lucide-react';
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider,} from '@workspace/active-ui/components/primitives/sidebar';
const MAIN = [ {label: 'Home', icon: HomeIcon}, {label: 'Participants', icon: UsersIcon, badge: '12'}, {label: 'Schemes', icon: FileTextIcon},];
/** * The parts of a sidebar, in a frame of its own: a header, a labelled group * with an action, menu buttons with icons and a badge, a submenu, and a * footer. `collapsible="none"` keeps it in place inside this example; the * navigation of this site is the same component in its full form, with the * sheet on small screens. */export default function SidebarExample() { const [current, setCurrent] = useState('Participants'); const pick = (label: string) => () => setCurrent(label); return ( <SidebarProvider className="min-h-0 w-auto"> <Sidebar collapsible="none" mobileLabel="Example navigation" className="h-96 rounded-lg border" > <SidebarHeader> <p className="px-2 py-1 font-semibold">Pension office</p> </SidebarHeader> <SidebarContent> <SidebarGroup> <SidebarGroupLabel>Work</SidebarGroupLabel> <SidebarGroupAction aria-label="Add a scheme" title="Add a scheme"> <PlusIcon aria-hidden /> </SidebarGroupAction> <SidebarGroupContent> <SidebarMenu> {MAIN.map(({label, icon: Icon, badge}) => ( <SidebarMenuItem key={label}> <SidebarMenuButton isActive={current === label} aria-current={current === label ? 'page' : undefined} onClick={pick(label)} > <Icon aria-hidden /> <span>{label}</span> </SidebarMenuButton> {badge ? ( <SidebarMenuBadge aria-label={`${badge} new`}> {badge} </SidebarMenuBadge> ) : null} </SidebarMenuItem> ))} <SidebarMenuItem> <SidebarMenuButton isActive={current.startsWith('Settings')} onClick={pick('Settings · General')} > <SettingsIcon aria-hidden /> <span>Settings</span> </SidebarMenuButton> <SidebarMenuSub> {['General', 'Users'].map((sub) => ( <SidebarMenuSubItem key={sub}> <SidebarMenuSubButton render={<button type="button" />} isActive={current === `Settings · ${sub}`} onClick={pick(`Settings · ${sub}`)} > {sub} </SidebarMenuSubButton> </SidebarMenuSubItem> ))} </SidebarMenuSub> </SidebarMenuItem> </SidebarMenu> </SidebarGroupContent> </SidebarGroup> </SidebarContent> <SidebarFooter> <p className="text-muted-foreground px-2 text-xs"> Showing: {current} </p> </SidebarFooter> </Sidebar> </SidebarProvider> );}Usage#
import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger,} from '@workspace/active-ui/components/primitives/sidebar';
<SidebarProvider> <Sidebar mobileLabel="Navigation"> <SidebarContent> <SidebarGroup> <SidebarGroupLabel>Work</SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton isActive render={<Link href="/schemes" />}> Schemes </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroupContent> </SidebarGroup> </SidebarContent> </Sidebar> <SidebarInset className="min-w-0"> <header> <SidebarTrigger label="Toggle navigation" /> </header> {page} </SidebarInset></SidebarProvider>;Layout#
Sidebarwraps the whole layout: it holds the open state, knows whether the screen is narrow (Provider use, belowIs Mobile 768px), and draws a flex row at least as high as the viewport.Sidebaris the column,16remwide (18remas a sheet):side:left(default) orright;variant:sidebar(default, flush with the edge),floating(a card with a margin) orinset(the page becomes the card);collapsible:offcanvas(default, slides out completely),icon(shrinks to3rem, icons only, labels as tooltips) ornone(always shown, as in the example).
Sidebaris the page beside it, aInset <main>. Give itmin-: it is a flex item, and without it the widest line of a page (a long code line, a wide table) sets its minimum width and pushes the page past the viewport on a narrow desktop.w- 0 Sidebartoggles the sidebar, or opens the sheet on a small screen.Trigger Sidebaris a thin handle on the sidebar's edge that toggles it too.Rail
The menu#
Sidebar,Header Sidebar(the scrolling part) andContent Sidebar.Footer SidebarwithGroup Sidebar,Group Label SidebarandGroup Action Sidebar.Group Content Sidebar(Menu <ul>) withSidebar(Menu Item <li>):Sidebar: the item.Menu Button ismarks the current page with a calm fill that stays while the pointer rests on another item; hover has half that fill, and keyboard focus its own ring.Active rendermakes your router's link the element;tooltipshows a label when the sidebar is collapsed to icons;sizeandvariantchange its height and look.Sidebar: a button at the end of an item (Menu Action showto reveal it on hover only).On Hover Sidebar: a count at the end of an item.Menu Badge SidebarwithMenu Sub SidebarandMenu Sub Item Sidebar: an indented submenu.Menu Sub Button
SidebarandInput Sidebar: a search field and a line, styled for the sidebar.Separator
State and the shortcut#
Uncontrolled by default (
default, defaultOpen true); controlled withopenandon. The sheet on a small screen has its own state.Open Change usereturns the state and its setters (Sidebar() state,open,set,Open open,Mobile set,Open Mobile is,Mobile toggle) to parts of your own inside the provider.Sidebar Ctrl+B(or⌘B) toggles the sidebar. The provider listens on the window, so use one provider per page.The provider stores nothing. To keep the open state across page loads, pass
persist: it is called with every new state, and you decide where it goes, such as a cookie your server reads back intoOpen State default.Open
What your application provides#
mobile: the accessible name of the sheet on a small screen (required), andLabel labelofSidebarandTrigger Sidebar.Rail The items, their links and the current page (
is, plusActive aria-on the link).current="page" Closing the sheet after a choice on a small screen: call
setwhen an item is chosen, as this site does.Open Mobile(false)
Accessibility and keyboard#
The menu items are links or buttons in lists;
Tabmoves through them.On a small screen the sidebar is a modal sheet named by
mobile: the focus moves into it, andLabel Escapecloses it.The current item is recognisable without colour alone through
aria-for screen readers; the fill is the visual cue.current
API#
SidebarProvider#
Import from @workspace/
Props
Events
Called by the component when something happens.
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
Sidebar#
Import from @workspace/
Props
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
SidebarMenuButton#
Import from @workspace/
Props marked Base UI are behaviour of the Base UI part this component is built on.
Props
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
Other parts#
SidebarMenuAction
Props marked Base UI are behaviour of the Base UI part this component is built on.
Props
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
SidebarMenuSubButton
Props marked Base UI are behaviour of the Base UI part this component is built on.
Props
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
SidebarRail
Props
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
SidebarSeparator
All props come from the Base UI part this component is built on.
Props
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
SidebarTrigger
Props marked Base UI are behaviour of the Base UI part this component is built on.
Props
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
Parts that take only their element’s attributes
Functions, hooks and constants
This page adapts the Sidebar documentation of shadcn/ui (MIT licence) to this package; see Third-party notices.