Sidebar
An app's navigation down the side: a workspace switcher, items, titled sections, groups that fold, and the pages visited lately.
import { Sidebar } from "@butteryui/react";Demo
When to use it
Use it for
- A dashboard's navigation, in an AppShell layout="sidebar"
- An app with more pages than a top bar holds, grouped into sections
Not for
- A marketing site's menus: use NavBar
- Sections of one long page: use SubNav
Examples
Dashboard nav
A dashboard's navigation: top pages, Recent, a titled section and a reports group that folds.
Roastery dashboard
A dashboard shell: the sidebar beside the top bar and the page, which follows the item chosen.
Home
Twelve orders to roast and pack today.
Router nav
A router takes over every item through onNavigate; the current page follows what it shows beside the nav.
Twelve orders to roast and pack today.
Variants and states
Navigation
Choose a page inside a group and it shows up under Recent; open and closed groups are remembered.
Props
Sidebar
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "Main" | Names the navigation for screen readers. Defaults to "Main". |
switcher | ReactNode | The workspace switcher at the top, such as an AccountMenu. | |
storageKey | string | Remembers in local storage which groups are open and the pages visited lately, under this key. | |
onNavigate | ((href: string, event: MouseEvent<HTMLAnchorElement>) => void) | Called when an item is chosen, with its href, unless the click opens a new tab. A router takes over
here: call event.preventDefault() and go to href. |
It also takes Omit<ComponentPropsWithRef<"nav">, "aria-label">.
Sidebar.Group
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | string | The group's name, on its header. In a rail it's the header's tooltip and name. | |
icon | ComponentType<IconComponentProps> | A 16px icon before the label. | |
defaultOpen | boolean | Whether it starts open. Defaults to whether it holds the page shown; once someone opens or closes it, the sidebar remembers. | |
children | ReactNode | Its items, Sidebar.Items. Written straight in the group (or in fragments), they count for Recent, the
rail's current mark and opening by default even while the group is closed; an item wrapped in a
component of your own counts once it's shown. |
Sidebar.Item
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | string | The page's name. In a rail it's the item's tooltip and name. | |
hrefrequired | string | Where it goes. | |
icon | ComponentType<IconComponentProps> | A 16px icon before the label. Items at the top of the nav have one; items in a group don't show it. | |
current | boolean | Whether it's the page shown. |
It also takes Omit<ComponentPropsWithRef<"a">, "children" | "href">.
Sidebar.Recent
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | "Recent" | The section's heading. Defaults to "Recent". |
Sidebar.Section
| Prop | Type | Default | Description |
|---|---|---|---|
titlerequired | string | The section's heading, which names it. |
It also takes ComponentPropsWithRef<"div">.
Also exported
| Export | Description |
|---|---|
useSidebarMode(): Exclude<SidebarMode, "sheet"> | undefined | How the Sidebar around a component shows: "full", or "rail" while the shell has made it an icon
rail; undefined outside a sidebar. A workspace switcher reads it to show its tile alone in a rail. |
Accessibility
A navigation named Main (or its label). Items are links with aria-current="page" on the page shown; sections are groups named by their headings; a group's header is a button with aria-expanded and aria-controls. In a rail each item keeps its name (a visually hidden label) and shows it as a tooltip, and a group's header opens a menu of links.
| Keys | What they do |
|---|---|
| Enter | Follows the focused item's link, through onNavigate when it's given |
| Enter or Space | Opens or closes the focused group |
| Enter, Space or Down | Opens a group's flyout menu in a rail, where Up and Down move, Enter follows a link and Escape closes |
Motion
It changes shape as one surface, on `spring.smooth`, or `spring.morph` for a big change, rather than closing and reopening.
With reduced motion: A group opens and closes at once, its chevron turning without animating.
Layout
It fills its parent's height: the switcher stays at the top while the nav scrolls under it, 12px in from the sides, items 2px apart. Each item is a --bt-h-sm (28px) row whose whole width is its hit area, its label truncating and shown in full on hover. In an AppShell it's 240px wide, a 56px icon rail under 1100px of shell and a sheet under 640.
Related
See it in the roastery demo.