# Sidebar

> Composable primitives for application sidebar navigation, with an icon-only collapsed mode.

Category: Navigation · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/sidebar

Sidebar provides the parts of a navigation sidebar (header, groups, items, footer and a collapse toggle) without positioning itself. Place it in your existing layout, fixed, sticky or in a grid, so product shells such as eSignify’s 18rem ↔ 5rem collapsing sidebar keep their structure. Use activeStyle="inverted" for a filled active item.

## Import

```tsx
import { Sidebar, SidebarHeader, SidebarContent, SidebarGroup, SidebarItem, SidebarFooter, SidebarToggle, useSidebar } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Basic

```tsx
import { Icon, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarHeader, SidebarItem } from '@bindshq/stance-react';

export function Basic() {
  return (
    <div style={{ height: 440, display: 'flex' }}>
      <Sidebar aria-label="Dashboard">
        <SidebarHeader>
          <strong>Binds</strong>
        </SidebarHeader>
        <SidebarContent>
          <SidebarGroup label="Workspace">
            <SidebarItem href="#dashboard" icon={<Icon name="home" />} active>
              Dashboard
            </SidebarItem>
            <SidebarItem href="#templates" icon={<Icon name="file-text" />}>
              My Templates
            </SidebarItem>
            <SidebarItem href="#users" icon={<Icon name="users" />}>
              Org Users
            </SidebarItem>
            <SidebarItem href="#integrations" icon={<Icon name="link" />}>
              Integrations
            </SidebarItem>
          </SidebarGroup>
        </SidebarContent>
        <SidebarFooter>
          <SidebarGroup>
            <SidebarItem href="#settings" icon={<Icon name="settings" />}>
              Settings
            </SidebarItem>
          </SidebarGroup>
        </SidebarFooter>
      </Sidebar>
    </div>
  );
}
```

### Collapsible (eSignify style)

Inverted active item; the toggle switches between 18rem and 5rem, showing icons with titles.

```tsx
import { useState } from 'react';
import { Icon, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarHeader, SidebarItem, SidebarToggle } from '@bindshq/stance-react';

export function Collapsible() {
  const [collapsed, setCollapsed] = useState(false);
  return (
    <div style={{ height: 440, display: 'flex' }}>
      <Sidebar
        aria-label="Dashboard"
        activeStyle="inverted"
        collapsed={collapsed}
        onCollapsedChange={setCollapsed}
      >
        <SidebarHeader style={{ justifyContent: collapsed ? 'center' : 'space-between' }}>
          {!collapsed && <strong>eSignify</strong>}
          <SidebarToggle />
        </SidebarHeader>
        <SidebarContent>
          <SidebarGroup>
            <SidebarItem href="#dashboard" icon={<Icon name="home" />} active>
              Dashboard
            </SidebarItem>
            <SidebarItem href="#templates" icon={<Icon name="file-text" />}>
              My Templates
            </SidebarItem>
            <SidebarItem href="#users" icon={<Icon name="users" />}>
              Org Users
            </SidebarItem>
            <SidebarItem href="#integrations" icon={<Icon name="link" />}>
              Integrations
            </SidebarItem>
            <SidebarItem href="#settings" icon={<Icon name="settings" />}>
              Settings
            </SidebarItem>
          </SidebarGroup>
        </SidebarContent>
        <SidebarFooter>
          <SidebarGroup>
            <SidebarItem icon={<Icon name="log-out" />} onClick={() => {}}>
              Log out
            </SidebarItem>
          </SidebarGroup>
        </SidebarFooter>
      </Sidebar>
    </div>
  );
}
```

### Collapsible groups and badges

```tsx
import { Icon, Sidebar, SidebarContent, SidebarGroup, SidebarItem } from '@bindshq/stance-react';

export function Groups() {
  return (
    <div style={{ height: 400, display: 'flex' }}>
      <Sidebar aria-label="Admin">
        <SidebarContent>
          <SidebarGroup label="Overview">
            <SidebarItem href="#dashboard" icon={<Icon name="bar-chart" />} active>
              Dashboard
            </SidebarItem>
            <SidebarItem href="#companies" icon={<Icon name="building" />} badge={12}>
              Companies
            </SidebarItem>
          </SidebarGroup>
          <SidebarGroup label="Library" collapsible>
            <SidebarItem href="#gallery" icon={<Icon name="image" />}>
              Image gallery
            </SidebarItem>
            <SidebarItem href="#templates" icon={<Icon name="layout-grid" />}>
              Global templates
            </SidebarItem>
          </SidebarGroup>
          <SidebarGroup label="Configuration" collapsible defaultOpen={false}>
            <SidebarItem href="#azure" icon={<Icon name="shield" />}>
              Azure setup
            </SidebarItem>
          </SidebarGroup>
        </SidebarContent>
      </Sidebar>
    </div>
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| Sidebar | `collapsed` | `boolean` | None | Controlled icon-only state. |
| Sidebar | `defaultCollapsed` | `boolean` | `false` | Initial collapsed state. |
| Sidebar | `onCollapsedChange` | `(collapsed: boolean) => void` | None | Called when the toggle is used. |
| Sidebar | `as` | `'nav' \| 'aside' \| 'div'` | `'nav'` | Root element; nav creates a navigation landmark. |
| Sidebar | `aria-label` | `string` | `'Main'` | Landmark name. |
| Sidebar | `activeStyle` | `'subtle' \| 'inverted'` | `'subtle'` | Tinted or filled active item. |
| Sidebar | `width` | `string` | `'18rem'` | Expanded width (sets --st-sidebar-width). |
| Sidebar | `collapsedWidth` | `string` | `'5rem'` | Collapsed width. |
| SidebarGroup | `label` | `ReactNode` | None | Group heading; names the list. |
| SidebarGroup | `collapsible` | `boolean` | `false` | Heading becomes a disclosure button. |
| SidebarGroup | `open / defaultOpen / onOpenChange` | `boolean / boolean / (open) => void` | `true` | Group open state. |
| SidebarItem | `icon` | `ReactNode` | None | Shown in both states; required for a useful collapsed sidebar. |
| SidebarItem | `active` | `boolean` | `false` | Current page: aria-current="page" on links. |
| SidebarItem | `badge` | `ReactNode` | None | Trailing count; becomes a dot when collapsed. |
| SidebarItem | `href` | `string` | None | Renders a link. Without href it renders a button. |
| SidebarItem | `asChild` | `boolean` | `false` | Render your router link. |
| SidebarItem | `disabled` | `boolean` | None | Disables a button item. |
| SidebarToggle | `expandLabel / collapseLabel` | `string` | `'Expand sidebar' / 'Collapse sidebar'` | Accessible labels for each state. |

## Accessibility

- The root is a named navigation landmark; each group is a list named by its heading.
- Collapsed mode hides labels visually only, so every item keeps its accessible name; a title shows the label on hover.
- The toggle reports aria-expanded and controls the sidebar; collapsible groups are disclosure buttons with aria-expanded.
- The active item is not conveyed by colour alone: it has aria-current and, in high contrast, an outline.

## Keyboard

| Keys | Action |
| --- | --- |
| `Tab` | Moves through items, group headings and the toggle. |
| `Enter` `Space` | Follows an item or toggles a group / the sidebar. |

## Guidelines

**Do**

- Give every item an icon if the sidebar can collapse.
- Persist the collapsed state per user.

**Don’t**

- Put more than about ten items in one group.
- Hide primary destinations inside collapsed groups by default.

## Related

- https://stanceui.com/components/tab-bar
- https://stanceui.com/components/breadcrumbs
- https://stanceui.com/components/navigation-menu
