Main content area
<Sidebar.Provider defaultOpen>
  <Sidebar>
    <Sidebar.Content>
      <Sidebar.Group>
        <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
        <Sidebar.Menu>
          <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>
          <Sidebar.MenuButton icon={GlobeIcon}>Domains</Sidebar.MenuButton>
        </Sidebar.Menu>
      </Sidebar.Group>
    </Sidebar.Content>
  </Sidebar>
</Sidebar.Provider>

Installation

Barrel

import { Sidebar } from "@cloudflare/kumo";

Granular

import { Sidebar } from "@cloudflare/kumo/components/sidebar";

Usage

At minimum you need Provider, Sidebar, Content (scrollable area), Menu, and MenuButton. Add Header / Footer to pin content above or below the scroll area. Use Group + GroupLabel to organize sections.

import { Sidebar } from "@cloudflare/kumo";
import { HouseIcon, CodeIcon, GearIcon } from "@phosphor-icons/react";

function AppLayout({ children }) {
  return (
    <Sidebar.Provider defaultOpen>
      <Sidebar>
        <Sidebar.Content>
          <Sidebar.Group>
            <Sidebar.GroupLabel>Navigation</Sidebar.GroupLabel>
            <Sidebar.Menu>
              <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>
              {/* MenuItem only needed to wrap Collapsible */}
              <Sidebar.MenuItem>
                <Sidebar.Collapsible>
                  <Sidebar.CollapsibleTrigger
                    render={
                      <Sidebar.MenuButton icon={CodeIcon}>
                        Compute <Sidebar.MenuChevron />
                      </Sidebar.MenuButton>
                    }
                  />
                  <Sidebar.CollapsibleContent>
                    <Sidebar.MenuSub>
                      <Sidebar.MenuSubButton>Workers</Sidebar.MenuSubButton>
                    </Sidebar.MenuSub>
                  </Sidebar.CollapsibleContent>
                </Sidebar.Collapsible>
              </Sidebar.MenuItem>
            </Sidebar.Menu>
          </Sidebar.Group>
        </Sidebar.Content>
        <Sidebar.Footer>
          <Sidebar.Trigger />
        </Sidebar.Footer>
      </Sidebar>
      <div className="flex-1">{children}</div>
    </Sidebar.Provider>
  );
}

Examples

Basic

The minimum viable sidebar: just groups, menu buttons, and collapsible sub-menus. No header or footer. MenuButton and MenuSubButton auto-wrap in <li> — no MenuItem / MenuSubItem needed.

Main content area
<Sidebar.Provider defaultOpen>
  <Sidebar>
    <Sidebar.Content>
      <Sidebar.Group>
        <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
        <Sidebar.Menu>
          <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>
          <Sidebar.MenuButton icon={ChartBarIcon}>Analytics</Sidebar.MenuButton>
        </Sidebar.Menu>
      </Sidebar.Group>
    </Sidebar.Content>
  </Sidebar>
</Sidebar.Provider>

Toggle & Collapsed State

Use Sidebar.Trigger in the footer or useSidebar().toggleSidebar programmatically. Pass tooltip to show labels on hover when collapsed.

Click the button or the sidebar trigger to toggle

<Sidebar.MenuButton icon={HouseIcon} tooltip="Home" active>
  Home
</Sidebar.MenuButton>

<Sidebar.Footer>
  <Sidebar.Trigger />
</Sidebar.Footer>

// Or programmatically:
const { toggleSidebar } = useSidebar();

Resizable

Drag the edge to resize. Dragging below minWidth collapses; dragging outward from collapsed expands. The resize handle is keyboard accessible: use arrow keys, Home, and End.

Drag the sidebar edge to resize

<Sidebar.Provider defaultOpen resizable defaultWidth={240} minWidth={180} maxWidth={400}>
  <Sidebar>
    <Sidebar.Content>...</Sidebar.Content>
    <Sidebar.ResizeHandle />
  </Sidebar>
</Sidebar.Provider>

Right Side

Use side="right" for a sidebar on the right edge. Place <main> before <Sidebar> in the DOM.

Main content area
<Sidebar.Provider defaultOpen side="right">
  <main className="flex-1">...</main>
  <Sidebar>
    <Sidebar.Content>
      <Sidebar.Group>
        <Sidebar.GroupLabel>Details</Sidebar.GroupLabel>
        <Sidebar.Menu>
          <Sidebar.MenuButton icon={GearIcon} active>Properties</Sidebar.MenuButton>
          <Sidebar.MenuButton icon={ChartBarIcon}>Metrics</Sidebar.MenuButton>
        </Sidebar.Menu>
      </Sidebar.Group>
    </Sidebar.Content>
  </Sidebar>
</Sidebar.Provider>

Peeking

Set peekable on the Provider. When the sidebar is collapsed, hovering or focusing it temporarily expands it. Moving away collapses it back. The data-state attribute will be "peeking" during the peek.

State: Expanded

Collapse, then hover the sidebar to peek

<Sidebar.Provider defaultOpen peekable>
  <Sidebar>
    <Sidebar.Content>...</Sidebar.Content>
    <Sidebar.Footer>
      <Sidebar.Trigger />
    </Sidebar.Footer>
  </Sidebar>
</Sidebar.Provider>

// Read peeking state:
const { state, isPeeking } = useSidebar();
// state: "expanded" | "collapsed" | "peeking"

Auto Scroll

Use autoScrollOnOpen on long collapsible sections to keep newly revealed content in view. This is useful when a group near the bottom of a scrollable sidebar expands below the visible area.

Open Workers near the bottom of the list

<Sidebar.Collapsible autoScrollOnOpen>
  <Sidebar.CollapsibleTrigger
    render={
      <Sidebar.MenuButton icon={CodeIcon}>
        Workers <Sidebar.MenuChevron />
      </Sidebar.MenuButton>
    }
  />
  <Sidebar.CollapsibleContent>
    <Sidebar.MenuSub>...</Sidebar.MenuSub>
  </Sidebar.CollapsibleContent>
</Sidebar.Collapsible>

Sliding Views

Use Sidebar.SlidingViews and Sidebar.SlidingView for animated horizontal transitions between navigation surfaces (e.g., account ↔ zone). Inactive views are automatically marked with aria-hidden and inert. Animation respects prefers-reduced-motion.

Active: Account surface

Click the header button to slide between views

const [surface, setSurface] = useState("account");

<Sidebar.SlidingViews activeKey={surface} direction="left">
  <Sidebar.SlidingView value="account">
    <Sidebar.Content>...account nav...</Sidebar.Content>
  </Sidebar.SlidingView>
  <Sidebar.SlidingView value="zone">
    <Sidebar.Content>...zone nav...</Sidebar.Content>
  </Sidebar.SlidingView>
</Sidebar.SlidingViews>

Full Example

Kitchen sink showcasing every subcomponent: header with account switcher, groups with labels, collapsible sections with nested expandable, badges, sliding views, and a footer trigger.

Main content area
<Sidebar>
  <Sidebar.Header>
    <AccountSwitcher />
  </Sidebar.Header>
  <Sidebar.Content>
    <Sidebar.Group>
      <Sidebar.Menu>
        <Sidebar.MenuButton icon={HouseIcon} active>Home</Sidebar.MenuButton>
      </Sidebar.Menu>
    </Sidebar.Group>
    <Sidebar.Group>
      <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>
      <Sidebar.Menu>
        <Sidebar.MenuItem>
          <Sidebar.Collapsible defaultOpen>
            <Sidebar.CollapsibleTrigger
              render={
                <Sidebar.MenuButton icon={CodeIcon}>
                  Compute <Sidebar.MenuChevron />
                </Sidebar.MenuButton>
              }
            />
            <Sidebar.CollapsibleContent>
              <Sidebar.MenuSub>
                <Sidebar.MenuSubButton>
                  Containers <Sidebar.MenuBadge>Beta</Sidebar.MenuBadge>
                </Sidebar.MenuSubButton>
              </Sidebar.MenuSub>
            </Sidebar.CollapsibleContent>
          </Sidebar.Collapsible>
        </Sidebar.MenuItem>
      </Sidebar.Menu>
    </Sidebar.Group>
  </Sidebar.Content>
  <Sidebar.Footer>
    <Sidebar.Trigger />
  </Sidebar.Footer>
</Sidebar>

Mobile

On narrow viewports the sidebar renders as a navigation drawer. Use mobileBreakpoint to control the threshold. The drawer uses inert and aria-hidden while closed, moves focus in on open, and supports Escape-to-close. This demo forces mobile mode via a high breakpoint.

Click the button to open the mobile sidebar

Press Escape or click the backdrop to close

<Sidebar.Provider mobileBreakpoint={9999}>
  <Sidebar>
    <Sidebar.Content>...</Sidebar.Content>
  </Sidebar>
</Sidebar.Provider>

Full-screen mobile

Pass fullScreenOnMobile to have the drawer cover the whole viewport instead of leaving a sliver of the page visible. Nav items get comfortable touch targets, and the backdrop is suppressed since nothing shows behind the sheet. Pair it with breadcrumbs in the page header so the current route stays visible once the nav is closed.

Account analytics

Drill into the nav — the trail is derived from the tree, so it always matches where you are.

<Sidebar.Provider mobileBreakpoint={9999}>
  <Sidebar fullScreenOnMobile>
    <Sidebar.Content>...</Sidebar.Content>
  </Sidebar>

  <header>
    <Sidebar.Trigger />
    <Breadcrumbs size="sm">
      <Breadcrumbs.Link href="/">Company</Breadcrumbs.Link>
      <Breadcrumbs.Separator />
      <Breadcrumbs.Current>Analytics</Breadcrumbs.Current>
    </Breadcrumbs>
  </header>
</Sidebar.Provider>

API Reference

Sidebar

The main sidebar container. Renders as <aside> on desktop and a navigation drawer on mobile.

PropTypeDefaultDescription
defaultOpenboolean-Initial open state when uncontrolled.
openboolean-Controlled open state.
variant"sidebar" | "floating" | "inset""sidebar"Sidebar layout variant.
side"left" | "right""left"Which side the sidebar is on.
collapsible"icon" | "offcanvas" | "none""icon"-
resizableboolean-Enable drag-to-resize on the sidebar edge.
defaultWidthnumber-Initial width in pixels when resizable.
minWidthnumber-Minimum width in pixels when resizing.
maxWidthnumber-Maximum width in pixels when resizing.
containedboolean-When true, the collapsed sidebar uses absolute positioning instead of fixed, keeping it scoped inside a bounded parent. Useful for demos and embedded sidebars.
peekableboolean-When true, hovering or focusing the collapsed sidebar temporarily expands it. The `state` will be `"peeking"` during the peek. Moving away collapses it back.
animationDurationnumber-Duration of sidebar expand/collapse animation in milliseconds.
mobileBreakpointnumber-Viewport width (in px) below which the sidebar renders as a mobile dialog sheet instead of the desktop aside rail.
childrenReactNode-Content — typically `<Sidebar>` + main content.
classNamestring-Additional CSS classes for the wrapper div.

Sidebar.Provider

Context provider managing expand/collapse state and mobile detection.

PropTypeDefaultDescription
defaultOpenboolean-Initial open state when uncontrolled.
openboolean-Controlled open state.
variantSidebarVariant-Sidebar layout variant.
sideSidebarSide-Which side the sidebar is on.
collapsible"icon" | "offcanvas" | "none"--
resizableboolean-Enable drag-to-resize on the sidebar edge.
defaultWidthnumber-Initial width in pixels when resizable.
minWidthnumber-Minimum width in pixels when resizing.
maxWidthnumber-Maximum width in pixels when resizing.
containedboolean-When true, the collapsed sidebar uses absolute positioning instead of fixed, keeping it scoped inside a bounded parent. Useful for demos and embedded sidebars.
peekableboolean-When true, hovering or focusing the collapsed sidebar temporarily expands it. The `state` will be `"peeking"` during the peek. Moving away collapses it back.
animationDurationnumber-Duration of sidebar expand/collapse animation in milliseconds.
mobileBreakpointnumber-Viewport width (in px) below which the sidebar renders as a mobile dialog sheet instead of the desktop aside rail.
children*ReactNode-Content — typically `<Sidebar>` + main content.
classNamestring-Additional CSS classes for the wrapper div.

Sidebar.Content

Scrollable middle section (flex-1 overflow-y-auto). Use Header / Footer to pin content above or below this scroll area.

Sidebar.MenuButton

Primary interactive element. Supports icons, active state, links, and auto-tooltip when collapsed. Auto-wraps in <li> — no MenuItem wrapper needed unless wrapping a Collapsible.

PropTypeDefaultDescription
iconReact.ComponentType<{ className?: string }> | React.ReactNode--
activeboolean--
sizeSidebarMenuButtonSize-Button size. - `"base"` — Standard nav item - `"sm"` — Compact nav item
hrefstring--
targetReact.HTMLAttributeAnchorTarget-Link target — only meaningful when `href` is provided.
tooltipstring--
classNamestring--
childrenReactNode--

Sidebar.MenuSubButton

Button inside a sub-menu for nested navigation. Auto-wraps in <li> — no MenuSubItem wrapper needed.

PropTypeDefaultDescription
activeboolean-Marks this sub-item as currently active/selected.
hrefstring-Navigation URL. When set, renders as a link via LinkProvider.
targetReact.HTMLAttributeAnchorTarget-Link target — only meaningful when `href` is provided.