# Sidebar

The collapsible navigation column of an application shell.

```tsx
import { BellIcon, FileTextIcon, LayoutDashboardIcon, ServerIcon, SettingsIcon, ShieldIcon } from "lucide-react"
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger } from "@zenoxai/ui/components/sidebar"

function SidebarDemo() {
  return (
    <div className="h-96 w-full overflow-hidden rounded-md border border-border">
      <SidebarProvider className="relative h-full min-h-0">
        <Sidebar collapsible="icon" className="absolute h-full">
          <SidebarHeader>
            <SidebarMenu>
              <SidebarMenuItem>
                <SidebarMenuButton
                  size="lg"
                  render={
                    <a href="#home">
                      <div className="flex size-8 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground">
                        <ShieldIcon />
                      </div>
                      <div className="flex flex-col">
                        <span className="font-medium">Vydar</span>
                        <span className="text-xs text-text-muted">
                          Acme Corp
                        </span>
                      </div>
                    </a>
                  }
                />
              </SidebarMenuItem>
            </SidebarMenu>
          </SidebarHeader>
          <SidebarContent>
            <SidebarGroup>
              <SidebarGroupLabel>Monitor</SidebarGroupLabel>
              <SidebarGroupContent>
                <SidebarMenu>
                  <SidebarMenuItem>
                    <SidebarMenuButton
                      tooltip="Dashboard"
                      render={
                        <a href="#dashboard">
                          <LayoutDashboardIcon />
                          <span>Dashboard</span>
                        </a>
                      }
                    />
                  </SidebarMenuItem>
                  <SidebarMenuItem>
                    <SidebarMenuButton
                      tooltip="Alerts"
                      isActive
                      render={
                        <a href="#alerts" aria-current="page">
                          <BellIcon />
                          <span>Alerts</span>
                        </a>
                      }
                    />
                    <SidebarMenuBadge>24</SidebarMenuBadge>
                  </SidebarMenuItem>
                  <SidebarMenuItem>
                    <SidebarMenuButton
                      tooltip="Assets"
                      render={
                        <a href="#assets">
                          <ServerIcon />
                          <span>Assets</span>
                        </a>
                      }
                    />
                  </SidebarMenuItem>
                </SidebarMenu>
              </SidebarGroupContent>
            </SidebarGroup>
            <SidebarGroup>
              <SidebarGroupLabel>Analyze</SidebarGroupLabel>
              <SidebarGroupContent>
                <SidebarMenu>
                  <SidebarMenuItem>
                    <SidebarMenuButton
                      tooltip="Reports"
                      render={
                        <a href="#reports">
                          <FileTextIcon />
                          <span>Reports</span>
                        </a>
                      }
                    />
                  </SidebarMenuItem>
                </SidebarMenu>
              </SidebarGroupContent>
            </SidebarGroup>
          </SidebarContent>
          <SidebarFooter>
            <SidebarMenu>
              <SidebarMenuItem>
                <SidebarMenuButton
                  tooltip="Settings"
                  render={
                    <a href="#settings">
                      <SettingsIcon />
                      <span>Settings</span>
                    </a>
                  }
                />
              </SidebarMenuItem>
            </SidebarMenu>
          </SidebarFooter>
        </Sidebar>
        <SidebarInset>
          <header className="flex h-12 items-center gap-2 border-b border-border px-3">
            <SidebarTrigger />
            <span className="text-sm font-medium text-text-primary">
              Alerts
            </span>
          </header>
          <div className="p-4 text-sm text-text-secondary">
            24 open alerts. Press Ctrl+B or Cmd+B to collapse the sidebar.
          </div>
        </SidebarInset>
      </SidebarProvider>
    </div>
  )
}
```

## Usage

```tsx
import {
  Sidebar,
  SidebarContent,
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarInset,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarProvider,
  SidebarTrigger,
} from "@zenoxai/ui/components/sidebar"
```

```tsx
<SidebarProvider>
  <Sidebar collapsible="icon">
    <SidebarContent>
      <SidebarGroup>
        <SidebarGroupLabel>Monitor</SidebarGroupLabel>
        <SidebarGroupContent>
          <SidebarMenu>
            <SidebarMenuItem>
              <SidebarMenuButton
                tooltip="Alerts"
                render={
                  <a href="/alerts">
                    <BellIcon />
                    <span>Alerts</span>
                  </a>
                }
              />
            </SidebarMenuItem>
          </SidebarMenu>
        </SidebarGroupContent>
      </SidebarGroup>
    </SidebarContent>
  </Sidebar>
  <SidebarInset>
    <SidebarTrigger />
    {children}
  </SidebarInset>
</SidebarProvider>
```

Zenox apps get their shell, sidebar included, from the platform package. Use this component
only to build the shell of a new app that does not use the platform package yet. It is the
shadcn/ui sidebar on Zenox tokens, and its API may change.

The parts nest in this order: `SidebarProvider` around the whole layout, then `Sidebar` and
`SidebarInset` side by side. Inside `Sidebar`, `SidebarHeader` and `SidebarFooter` stay put
while `SidebarContent` scrolls. Group links with `SidebarGroup` and a `SidebarGroupLabel`, and
list them with `SidebarMenu`, `SidebarMenuItem` and `SidebarMenuButton`.

- `collapsible`: `offcanvas` (default) slides the sidebar out of view, `icon` shrinks it to a
  column of icons, `none` keeps it fixed.
- `variant`: `sidebar` (default), `floating` or `inset`.
- `SidebarMenuButton` renders a `<button>`. For navigation, pass a link with its icon and
  label to `render`, as above. Set `tooltip` so the label stays available when the sidebar
  collapses to icons.
- `isActive` marks the current page with the selection colors, a primary-soft fill and text.
  Hover and press use the neutral `surface-muted` fill, so a hovered entry never looks like
  the current one (`zx/selected-state` in [Layout and craft](/guidelines/layout-and-craft)).
- `size` on `SidebarMenuButton` sets the row height: `sm` 28px, `default` 32px, `lg` 48px for
  two-line entries. The text stays at the 12.5px body size in every size, and in both sizes
  of `SidebarMenuSubButton`.
- `SidebarProvider` takes `defaultOpen`, or `open` and `onOpenChange` to control the state. It
  stores the state in the `sidebar_state` cookie; read it on the server to render the sidebar
  in the state the user left it.

The demos on this page override the full-height layout (`relative h-full min-h-0` on the
provider, `absolute h-full` on `Sidebar`) so the sidebar fits in a box. In an app, render the
provider at the root and leave those classes out.

## Examples

### Sub-items and group action

`SidebarMenuSub` nests links under an item. `SidebarGroupAction` adds an icon button to the
group label; give it an `aria-label`.

```tsx
import { GlobeIcon, PlusIcon, ServerIcon } from "lucide-react"
import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider } from "@zenoxai/ui/components/sidebar"

function SidebarWithSubmenu() {
  return (
    <div className="h-64 w-full overflow-hidden rounded-md border border-border">
      <SidebarProvider className="h-full min-h-0">
        <Sidebar collapsible="none" className="border-r border-border">
          <SidebarContent>
            <SidebarGroup>
              <SidebarGroupLabel>Inventory</SidebarGroupLabel>
              <SidebarGroupAction aria-label="Add asset">
                <PlusIcon />
              </SidebarGroupAction>
              <SidebarGroupContent>
                <SidebarMenu>
                  <SidebarMenuItem>
                    <SidebarMenuButton
                      render={
                        <a href="#assets">
                          <ServerIcon />
                          <span>Assets</span>
                        </a>
                      }
                    />
                    <SidebarMenuSub>
                      <SidebarMenuSubItem>
                        <SidebarMenuSubButton
                          href="#domains"
                          isActive
                          aria-current="page"
                        >
                          <GlobeIcon />
                          <span>Domains</span>
                        </SidebarMenuSubButton>
                      </SidebarMenuSubItem>
                      <SidebarMenuSubItem>
                        <SidebarMenuSubButton href="#hosts">
                          <span>Hosts</span>
                        </SidebarMenuSubButton>
                      </SidebarMenuSubItem>
                      <SidebarMenuSubItem>
                        <SidebarMenuSubButton href="#cloud-accounts">
                          <span>Cloud accounts</span>
                        </SidebarMenuSubButton>
                      </SidebarMenuSubItem>
                    </SidebarMenuSub>
                  </SidebarMenuItem>
                </SidebarMenu>
              </SidebarGroupContent>
            </SidebarGroup>
          </SidebarContent>
        </Sidebar>
        <SidebarInset>
          <div className="p-4 text-sm text-text-secondary">
            142 domains across 3 tenants.
          </div>
        </SidebarInset>
      </SidebarProvider>
    </div>
  )
}
```

## Accessibility

- `SidebarTrigger` toggles the sidebar, and so does Ctrl+B or Cmd+B, except while typing in a
  text field.
  Below 768px the sidebar renders as a [Sheet](/components/sheet), with focus trapped inside
  while it is open.
- `isActive` highlights the entry and sets `aria-current="page"`: set it on the current page.
- The labels of `SidebarTrigger`, `SidebarRail` and the mobile sheet are fixed English strings.
  Pass a translated `aria-label` to `SidebarTrigger` and `SidebarRail` when the interface is
  not in English.

## API reference

`import { ... } from "@zenoxai/ui/components/sidebar"`

### Sidebar

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| collapsible | `"none" \| "icon" \| "offcanvas"` | `offcanvas` | How it collapses on desktop: `offcanvas` (default) slides it out of view; `icon` shrinks it to a 48px rail of icons, with SidebarMenuButton tooltips; `none` keeps it open. |
| side | `"left" \| "right"` | `left` | Edge of the screen it sits on. Defaults to left. |
| variant | `"inset" \| "sidebar" \| "floating"` | `sidebar` | `sidebar` (default) is flush with the screen edge, behind a border; `floating` is a rounded panel inset from the edges; `inset` renders the SidebarInset beside it as a rounded card. |

### SidebarContent

No props of its own.

### SidebarFooter

No props of its own.

### SidebarGroup

No props of its own.

### SidebarGroupAction

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

### SidebarGroupContent

No props of its own.

### SidebarGroupLabel

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

### SidebarHeader

No props of its own.

### SidebarInput

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| size | `"default" \| "sm" \| "lg"` |  | Height: `sm` 28px, `default` 32px, `lg` 36px, the heights of the Button sizes of the same names. Replaces the native `size` attribute (a width in characters); set the width with a class instead. |

### SidebarInset

No props of its own.

### SidebarMenu

No props of its own.

### SidebarMenuAction

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| showOnHover | `boolean` | `false` | On desktop, hides it until the entry is hovered or focused. |

### SidebarMenuBadge

No props of its own.

### SidebarMenuButton

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| isActive | `boolean` | `false` | Marks the entry as the current page: highlight and aria-current="page". |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| size | `"default" \| "sm" \| "lg" \| null` | `default` | Height: `sm` 28px, `default` 32px, `lg` 48px, for an entry with two lines such as a workspace switcher. Every size uses the 12.5px body text. |
| tooltip | `string \| (TooltipPopupProps & Pick<TooltipPositionerProps, "align" \| "side" \| "sideOffset" \| "alignOffset">)` |  | Label shown beside the entry while the sidebar is collapsed to icons, as a string or as TooltipContent props. |
| variant | `"default" \| "outline" \| null` | `default` | `default` has no fill until hover; `outline` adds a background and a border. |

### SidebarMenuItem

No props of its own.

### SidebarMenuSkeleton

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| showIcon | `boolean` | `false` | Adds a square placeholder for the icon. |

### SidebarMenuSub

No props of its own.

### SidebarMenuSubButton

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| isActive | `boolean` | `false` | Marks the entry as the current page: highlight and aria-current="page". |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| size | `"sm" \| "md"` | `md` | Kept for compatibility with the shadcn API: `md` (default) and `sm` both render a 28px row with the 12.5px body text, since smaller text is hard to read in navigation. The value is exposed as `data-size`. |

### SidebarMenuSubItem

No props of its own.

### SidebarProvider

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| defaultOpen | `boolean` | `true` | Initial desktop state when uncontrolled. Defaults to true (open). |
| onOpenChange | `((open: boolean) => void)` |  | Called with the new desktop state. The mobile sheet is not reported. |
| open | `boolean` |  | Controlled desktop state; pair it with `onOpenChange`. |

### SidebarRail

No props of its own.

### SidebarSeparator

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| orientation | `"horizontal" \| "vertical"` | `vertical` | The orientation of the separator. |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, SeparatorState>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

### SidebarTrigger

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| focusableWhenDisabled | `boolean` | `false` | Whether the button should be focusable when disabled. |
| nativeButton | `boolean` | `true` | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, ButtonState>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| size | `"default" \| "xs" \| "sm" \| "lg" \| "icon" \| "icon-xs" \| "icon-sm" \| "icon-lg" \| null` |  | Height: `xs` 24px, `sm` 28px, `default` 32px, `lg` 36px. The `icon-*` sizes are square buttons of the same heights for a single icon. The icon follows the size name: 12px at `xs`, 14px at `sm`, 16px at `default` and `lg`. |
| variant | `"link" \| "default" \| "destructive" \| "outline" \| "secondary" \| "ghost" \| null` |  | Visual weight. `default` is the filled primary action, one per view; `outline` and `secondary` are for the other actions; `ghost` has no fill until hover, for toolbars and icon buttons; `destructive` is for delete and other irreversible actions; `link` looks like inline text. |

### useSidebar

No props of its own.

