Components

Sidebar

Headless app-sidebar primitives for expanded, rail, and offcanvas layout states.

When to Use

Use Sidebar for a persistent app area beside the main content, such as workspace navigation or tools, that may collapse to icons or move off screen. Use Drawer for temporary content that overlays the page and needs modal focus management. Sidebar owns layout state and relationships, not visual sizing.

Features

  • Supports controlled and uncontrolled sidebar state.
  • Supports expanded, rail, and offcanvas states.
  • Supports left and right sidebars.
  • Provides trigger, panel, and main content relationships.
  • Keeps layout and visual sizing in consumer styles.
  • Removes offcanvas panels from the accessibility tree.

Import

import { Sidebar } from "@flowstack-ui/atom";

Anatomy

<Sidebar.Root>
  <Sidebar.Trigger />
  <Sidebar.Panel />
  <Sidebar.Main />
</Sidebar.Root>

API Reference

Root

Owns the expanded, rail, or offcanvas state and shares generated Trigger/Panel relationships with every part. It renders a div by default.

PropTypeDefault
childrenReactNode-
state"expanded" | "rail" | "offcanvas"-
defaultState"expanded" | "rail" | "offcanvas""expanded"
onStateChange(state) => void-
collapsedState"rail" | "offcanvas""offcanvas"
side"left" | "right""left"
disabledbooleanfalse
asChildbooleanfalse
renderRenderProp-

ARIA: Root adds no role or ARIA attributes.

Data attributeValues
[data-slot]"sidebar"
[data-state]"expanded" | "rail" | "offcanvas"
[data-side]"left" | "right"
[data-collapsed-state]"rail" | "offcanvas"
[data-disabled]Present when disabled

When disabled, Root ignores state changes and descendant Trigger parts render disabled state.

Trigger

Toggles the sidebar between expanded and the configured collapsed state. Renders a button with type="button" by default. Disabled state comes from Sidebar.Root disabled; Trigger does not expose its own disabled prop.

PropTypeDefault
toState"expanded" | "rail" | "offcanvas"toggles between "expanded" and collapsedState
childrenReactNode-
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-controlsGenerated Panel id
aria-expandedtrue when state is "expanded"
aria-disabledtrue when Root is disabled
Data attributeValues
[data-slot]"sidebar-trigger"
[data-state]"expanded" | "rail" | "offcanvas"
[data-side]"left" | "right"
[data-collapsed-state]"rail" | "offcanvas"
[data-target-state]State that activation will request
[data-disabled]Present when disabled

Panel

Contains the sidebar's navigation or tools and renders an aside by default. It becomes inert and leaves the accessibility tree only in offcanvas state.

PropTypeDefault
childrenReactNode-
asChildbooleanfalse
renderRenderProp-
aria-labelstring-
aria-labelledbystring-
ARIA attributeValues
aria-hiddentrue when state is "offcanvas"
inertPresent when state is "offcanvas"
Data attributeValues
[data-slot]"sidebar-panel"
[data-state]"expanded" | "rail" | "offcanvas"
[data-side]"left" | "right"
[data-collapsed-state]"rail" | "offcanvas"
[data-disabled]Present when disabled

Main

Renders the main content region associated with the sidebar. Renders a main by default. It receives the same state metadata as Panel so a consumer can coordinate the surrounding layout.

PropTypeDefault
childrenReactNode-
asChildbooleanfalse
renderRenderProp-

ARIA: Main uses the native <main> landmark by default and adds no ARIA attributes.

Data attributeValues
[data-slot]"sidebar-main"
[data-state]"expanded" | "rail" | "offcanvas"
[data-side]"left" | "right"
[data-collapsed-state]"rail" | "offcanvas"
[data-disabled]Present when disabled

Advanced compound parts can read state and actions with useSidebarContext; SidebarContextProvider and the context value type are also public for low-level composition.

Examples

Responsive Sidebar

import { Sidebar } from "@flowstack-ui/atom";

export default function ResponsiveSidebar() {
  return (
    <Sidebar.Root collapsedState="offcanvas">
      <Sidebar.Trigger>Menu</Sidebar.Trigger>
      <Sidebar.Panel aria-label="Primary navigation">
        <nav><a href="/projects">Projects</a></nav>
      </Sidebar.Panel>
      <Sidebar.Main>Main content</Sidebar.Main>
    </Sidebar.Root>
  );
}

Rail Sidebar

import { Sidebar } from "@flowstack-ui/atom";

export default function RailSidebar() {
  return (
    <Sidebar.Root collapsedState="rail" defaultState="rail">
      <Sidebar.Trigger>Toggle navigation</Sidebar.Trigger>
      <Sidebar.Panel aria-label="Primary navigation">Navigation</Sidebar.Panel>
      <Sidebar.Main>Content</Sidebar.Main>
    </Sidebar.Root>
  );
}

Accessibility

Sidebar uses native button, complementary, and main landmarks rather than a special WAI-ARIA widget pattern. Add a Panel name when a page has multiple complementary landmarks. Trigger exposes aria-expanded and aria-controls; offcanvas panels are aria-hidden and inert so their controls cannot be reached.

KeyDescription
Enter / SpaceActivates Sidebar.Trigger
TabMoves through focusable content in the visible sidebar and main content

Changelog

See CHANGELOG.md.