nori-ui

Sidebar

Collapsible side-panel navigation with compound API — Header, Content, Footer, Group, GroupLabel, Menu, MenuItem.

3.5 kBgzipped

At a glance

  • Compound API: Sidebar, Sidebar.Header, Sidebar.Content, Sidebar.Footer, Sidebar.Group, Sidebar.GroupLabel, Sidebar.Menu, Sidebar.MenuItem.
  • Web: fixed <aside> with role="navigation". Width transitions between 240 px (expanded) and 56 px (collapsed — icons only).
  • Native v1: always-visible View. Slide-in drawer behavior is a v2 follow-up.
  • Accessibility: role="navigation" + aria-label. Active item gets aria-current="page". Collapsed label surfaced as title tooltip on web.

Preview

Open / collapsed state

collapsed (controlled) + onCollapsedChange is the parent-driven shape.
Use defaultCollapsed for uncontrolled mode.

import { Sidebar } from '@nori-ui/core';
 
// Uncontrolled — starts expanded.
<Sidebar defaultCollapsed={false}>
  {/* ... */}
</Sidebar>
 
// Controlled — parent owns state.
const [collapsed, setCollapsed] = useState(false);
<Sidebar collapsed={collapsed} onCollapsedChange={setCollapsed}>
  {/* ... */}
</Sidebar>

Anatomy

SubcomponentRole
SidebarRoot — owns collapsed state, wraps <aside role="navigation"> on web.
Sidebar.HeaderTop slot — logo, workspace switcher, etc.
Sidebar.ContentScrollable middle zone — holds Groups.
Sidebar.FooterPinned bottom slot — user row, logout.
Sidebar.GroupLogical section inside Content.
Sidebar.GroupLabelSection heading. Hidden when collapsed.
Sidebar.MenuOrdered list of MenuItem entries.
Sidebar.MenuItemSingle tappable nav entry with optional icon and active state.

Full example

import { Sidebar } from '@nori-ui/core';
 
<Sidebar defaultCollapsed={false}>
  <Sidebar.Header>
    <Avatar src="..." />
    <Text>Acme Inc.</Text>
  </Sidebar.Header>
  <Sidebar.Content>
    <Sidebar.Group>
      <Sidebar.GroupLabel>Main</Sidebar.GroupLabel>
      <Sidebar.Menu>
        <Sidebar.MenuItem icon={<HomeIcon />} onPress={() => navigate('/')}>
          Home
        </Sidebar.MenuItem>
        <Sidebar.MenuItem icon={<UsersIcon />} active>
          Team
        </Sidebar.MenuItem>
      </Sidebar.Menu>
    </Sidebar.Group>
    <Sidebar.Group>
      <Sidebar.GroupLabel>Settings</Sidebar.GroupLabel>
      <Sidebar.Menu>
        <Sidebar.MenuItem icon={<CogIcon />} onPress={() => navigate('/settings')}>
          Preferences
        </Sidebar.MenuItem>
      </Sidebar.Menu>
    </Sidebar.Group>
  </Sidebar.Content>
  <Sidebar.Footer>
    <Button onPress={logout}>Logout</Button>
  </Sidebar.Footer>
</Sidebar>

Collapsed mode (web)

When collapsed={true} the sidebar narrows to 56 px. Labels are hidden; only icons remain visible. On web, the icon button receives a title attribute equal to the label string, which browsers surface as a native tooltip on hover.

// Icons are required in collapsed mode for usability.
<Sidebar.MenuItem icon={<HomeIcon />} onPress={goHome}>
  Home
</Sidebar.MenuItem>

Variants

VariantDescription
standard (default)Full-bleed panel attached to the screen edge. Right border separates it from content.
insetStandard with a subtle box-shadow instead of a border.
floatingHas margin + rounded corners — visually floats above the page.
<Sidebar variant="floating">
  {/* ... */}
</Sidebar>

Side

The side prop controls which edge the sidebar attaches to. Defaults to 'left'.

<Sidebar side="right">
  {/* ... */}
</Sidebar>

Props

PropTypeDefaultDescription
classNamestring
collapsedbooleanControlled collapsed state.
defaultCollapsedbooleanfalseUncontrolled initial collapsed state. @defaultValue false
onCollapsedChange(collapsed: boolean) => voidFires with the new collapsed state.
sideenumleftWhich edge the panel is attached to. @defaultValue 'left'
testIDstring
variantenumstandardVisual variant. @defaultValue 'standard'

Sidebar.MenuItem

A single tappable navigation row inside Sidebar.Menu. Accepts:

  • children — the item's text label. Hidden (visually) when the sidebar is collapsed; still read by screen readers.
  • icon — a React element rendered to the left of the label. Required in collapsed mode for usability.
  • active — marks the item as the current page. Applies an active style and sets aria-current="page".
  • onPress — press handler. The item is tappable when this is provided.
  • disabled — prevents interaction and dims the row.
  • className — custom styles for the row element.

v2 / deferred

  • Native slide-in drawer — on mobile, a real drawer with swipe-to-open/close gesture is the idiomatic pattern. v1 ships as an always-visible View.
  • Collapsible-on-hover variant — auto-collapse when the user leaves the sidebar; auto-expand on hover.

On this page

Preview theme