1. Documentation
  2. Layout
  3. Sidebar
ReadmenpmGitHub
  • Introduction
  • Installation
  • Theming
  • Icons
  • Accordion
  • Card
  • Scroll Area
  • Separator
  • Sidebar
  • Field
  • Label
  • Input
  • Textarea
  • Number Field
  • OTP Field
  • Select
  • Combobox
  • Checkbox
  • Radio
  • Switch
  • Slider
  • Toggle
  • Calendar
  • Date Picker
  • File Upload
  • Button
  • Copy Button
  • Dropdown
  • Command
  • Tabs
  • Breadcrumb
  • Pagination
  • Tree
  • Table
  • Data Table
  • Stat
  • Timeline
  • Avatar
  • Status Bar
  • Badge
  • Meter
  • Empty
  • Skeleton
  • Spinner
  • Toast
  • Dialog
  • Sheet
  • Popover
  • Tooltip
  • Text
  • Kbd
  • Code Block
  • Utilities
  • Design rules

Sidebar

Composable application chrome — collapsible to a rail, keyboard-toggleable, an overlay drawer on a phone.

Loading documentation…

Separator< PreviousFieldNext >

Powered by heyo

On this page

UsageNested sectionsRouter linksCollapsingReading the statePropertiesSidebar.ProviderSidebarSidebar.MenuButton and Sidebar.MenuSubButtonStructural parts
h

heyo-ui

Platform

Overview

production

Select the trigger — or the hairline seam between the panel and this column — to collapse the sidebar into its rail.

<Sidebar.Provider defaultOpen><Sidebar>  <Sidebar.Header>    <div className="flex items-center gap-2 px-1 py-0.5">      <div className="grid size-6 shrink-0 place-items-center rounded-md bg-heyo-contrast text-heyo-inverse">        <span className="font-mono text-[11px] font-semibold">h</span>      </div>      <Sidebar.Label className="text-sm font-medium text-heyo-strong">        heyo-ui      </Sidebar.Label>      <Sidebar.Trigger className="ml-auto" />    </div>    <Input      size="sm"      icon={IconSearch}      placeholder="Filter components…"      aria-label="Filter components"      className="group-data-[state=collapsed]/sidebar:hidden"    />  </Sidebar.Header>  <Sidebar.Content>    <Sidebar.Group>      <Sidebar.GroupLabel>Platform</Sidebar.GroupLabel>      <Sidebar.Menu>        <Sidebar.MenuButton icon={IconHome} active>Overview</Sidebar.MenuButton>        <Sidebar.MenuItem>          <Sidebar.Collapsible defaultOpen>            <Sidebar.CollapsibleTrigger              render={                <Sidebar.MenuButton unwrapped icon={IconCube}>                  Compute                  <Sidebar.MenuBadge>2</Sidebar.MenuBadge>                  <Sidebar.MenuChevron />                </Sidebar.MenuButton>              }            />            <Sidebar.CollapsibleContent>              <Sidebar.MenuSub>                <Sidebar.MenuSubButton icon={IconBolt} render={<a href="#workers" />}>                  Workers                </Sidebar.MenuSubButton>                <Sidebar.MenuSubButton icon={IconBrowser} render={<a href="#pages" />}>                  Pages                </Sidebar.MenuSubButton>              </Sidebar.MenuSub>            </Sidebar.CollapsibleContent>          </Sidebar.Collapsible>        </Sidebar.MenuItem>        <Sidebar.MenuButton icon={IconDatabase}>Storage</Sidebar.MenuButton>      </Sidebar.Menu>    </Sidebar.Group>  </Sidebar.Content>  <Sidebar.Footer>    <Sidebar.Menu>      <Sidebar.MenuButton icon={IconMoon}>Dark mode</Sidebar.MenuButton>    </Sidebar.Menu>  </Sidebar.Footer>  <Sidebar.Rail /></Sidebar><Sidebar.Inset>  <header className="flex h-12 shrink-0 items-center gap-3 border-b border-heyo-hairline px-5">    <Heading level={4}>Overview</Heading>    <Badge variant="outline" size="sm">production</Badge>  </header>  <div className="p-5">{children}</div></Sidebar.Inset></Sidebar.Provider>

Sidebar is the application chrome: a provider that owns the open state, a panel composed from menus and groups, and an inset that holds the page. It collapses to a rail of icons, toggles with ⌘B / Ctrl B, and turns into an overlay drawer below 768px on its own.

It is deliberately not a panel. The sidebar shares the page background and a single hairline separates it — a sidebar lighter than the page reads as something floating on top of the application rather than part of it.

Usage

tsx
import { Sidebar } from "@heyo-sh/heyo-ui";<Sidebar.Provider defaultOpen>  <Sidebar>    <Sidebar.Content>      <Sidebar.Menu>        <Sidebar.MenuButton icon={IconHome} active>          Home        </Sidebar.MenuButton>      </Sidebar.Menu>    </Sidebar.Content>  </Sidebar>  <Sidebar.Inset>{children}</Sidebar.Inset></Sidebar.Provider>;

Sidebar.Provider wraps both the sidebar and the page, because the inset reacts to the same state.

Nested sections

A branch is a Sidebar.Collapsible inside a Sidebar.MenuItem. MenuButton wraps itself in an <li> by default, so pass unwrapped when you supply the item yourself — as the trigger above does.

Platform

Workers

<Sidebar.MenuItem><Sidebar.Collapsible defaultOpen>  <Sidebar.CollapsibleTrigger    render={      <Sidebar.MenuButton unwrapped icon={IconCube}>        Compute        <Sidebar.MenuChevron />      </Sidebar.MenuButton>    }  />  <Sidebar.CollapsibleContent>    <Sidebar.MenuSub>      <Sidebar.MenuSubButton active>Workers</Sidebar.MenuSubButton>      <Sidebar.MenuSubButton>Pages</Sidebar.MenuSubButton>    </Sidebar.MenuSub>  </Sidebar.CollapsibleContent></Sidebar.Collapsible></Sidebar.MenuItem>

Router links

Every menu button takes render, so a navigation entry becomes whatever your router uses while keeping the row's styling, active state and icon slot.

tsx
<Sidebar.MenuButton icon={IconHome} render={<NavLink to="/" />}>  Home</Sidebar.MenuButton>

Collapsing

icondefaultoptional

Shrinks to a rail: labels and badges fade out, icons stay. The reader keeps a map of the application.

offcanvascollapsibleoptional

Slides fully out of view. Use it when the content needs every pixel.

nonecollapsibleoptional

Always expanded — for a sidebar that is the whole interface.

Sidebar.Rail turns the hairline seam into a click target, which is what makes the whole thing feel like real application chrome rather than a navigation list. Anything that should disappear with the labels gets group-data-[state=collapsed]/sidebar:hidden — the search field in the first example does exactly that.

Reading the state

tsx
import { useSidebar } from "@heyo-sh/heyo-ui";const { open, setOpen, toggle, state, isMobile } = useSidebar();

Anything below the provider can read or drive it — a header button, a keyboard handler of your own, a layout that shifts when the rail collapses.

Properties

Sidebar.Provider

defaultOpenbooleanoptional

Uncontrolled initial state. It defaults to true.

openbooleanoptional

Controlled state. Pair it with onOpenChange.

onOpenChange(open: boolean) => voidoptional

Fires on every toggle, including the keyboard shortcut.

widthstringoptional

Expanded width, any CSS length. It defaults to "15rem".

widthIconstringoptional

Collapsed rail width. It defaults to "3.25rem".

shortcutstring | falseoptional

The key that toggles the sidebar together with ⌘ or Ctrl. It defaults to "b"; pass false to opt out.

mobileBreakpointstringoptional

The media query below which the sidebar becomes an overlay drawer. It defaults to "(max-width: 767px)".

Sidebar

side"left" | "right"optional

Which edge it sits on. It defaults to left.

collapsible"icon" | "offcanvas" | "none"optional

How it collapses. It defaults to icon.

Sidebar.MenuButton and Sidebar.MenuSubButton

iconIconLikeoptional

The leading glyph. It stays visible when the rail collapses.

activebooleanoptional

Marks the entry as the current page.

renderRenderPropoptional

Swaps the <button> for a link or a router component.

unwrappedbooleanoptional

Skips the automatic <li> wrapper, for when you supply your own Sidebar.MenuItem — needed around a Collapsible.

Structural parts

Sidebar.Header / Content / Footerdivoptional

The three stacked regions. Content scrolls with the restyled native scrollbar; Header and Footer stay put.

Sidebar.Group / GroupLabeldivoptional

A titled section. The label fades out as the rail collapses.

Sidebar.Menu / MenuItem / MenuSub / MenuSubItemul, lioptional

The list structure. Menu is a <ul>; the buttons supply their own <li> unless you pass unwrapped.

Sidebar.MenuBadgeBadgePropsoptional

The trailing chip on a row — literally <Badge variant="count">, with the one rule the sidebar adds: it disappears when the rail collapses.

Sidebar.MenuChevronspanoptional

The rotating chevron for a collapsible branch.

Sidebar.Collapsible / CollapsibleTrigger / CollapsibleContent—optional

A nested section wired to the rail animation.

Sidebar.Triggerbuttonoptional

The toggle. Takes icon to replace the default glyph.

Sidebar.Railbuttonoptional

The hairline seam, doubling as a click target.

Sidebar.Insetmainoptional

The content column that sits next to the sidebar.

Sidebar.Labelspanoptional

Text that fades out as the rail collapses. Exported so a custom row can join the same animation.