# App Sidebar

> Nested navigation with collapsed mode, a mobile disclosure, and active states.

- Category: Navigation
- Kind: component
- Page: https://uaiblocks.dev/components/app-sidebar
- Registry item: https://uaiblocks.dev/r/app-sidebar.json
- Source: https://github.com/victor-teles/uai/blob/main/src/registry/uai/components/app-sidebar.tsx

## Install

```bash
bunx shadcn@latest add https://uaiblocks.dev/r/app-sidebar.json
```

## Usage

```tsx
"use client";

import { Bell, CircleHelp, FileText, Inbox, LayoutDashboard, Settings, Users } from "lucide-react";
import { useState } from "react";
import {
  AppSidebar,
  AppSidebarCollapseToggle,
  AppSidebarFooter,
  AppSidebarGroup,
  AppSidebarGroupLabel,
  AppSidebarHeader,
  AppSidebarItem,
  AppSidebarItemBadge,
  AppSidebarItemIcon,
  AppSidebarItemLabel,
  AppSidebarList,
  AppSidebarMobileTrigger,
  AppSidebarNav,
  AppSidebarSubmenu,
  AppSidebarSubmenuList,
  AppSidebarSubmenuTrigger,
  AppSidebarTitle,
  type AppSidebarVariant,
} from "@/components/ui/uai/app-sidebar";

const pages: Record<string, string> = {
  overview: "Overview",
  inbox: "Inbox",
  briefs: "Project briefs",
  contracts: "Contracts",
  team: "Team",
  alerts: "Notifications",
  settings: "Settings",
  help: "Help center",
};

export function AppSidebarPreview({ variant = "panel" }: { variant?: AppSidebarVariant }) {
  const [page, setPage] = useState("overview");
  return (
    <div style={{ display: "flex", height: 460, gap: 16 }}>
      <AppSidebar variant={variant} value={page} onValueChange={setPage}>
        <AppSidebarHeader>
          <AppSidebarTitle>Northwind Studio</AppSidebarTitle>
          <AppSidebarCollapseToggle />
          <AppSidebarMobileTrigger />
        </AppSidebarHeader>
        <AppSidebarNav>
          <AppSidebarGroup>
            <AppSidebarGroupLabel>Workspace</AppSidebarGroupLabel>
            <AppSidebarList>
              <AppSidebarItem value="overview" label="Overview" onClick={(e) => e.preventDefault()}>
                <AppSidebarItemIcon>
                  <LayoutDashboard size={16} />
                </AppSidebarItemIcon>
                <AppSidebarItemLabel>Overview</AppSidebarItemLabel>
              </AppSidebarItem>
              <AppSidebarItem value="inbox" label="Inbox" onClick={(e) => e.preventDefault()}>
                <AppSidebarItemIcon>
                  <Inbox size={16} />
                </AppSidebarItemIcon>
                <AppSidebarItemLabel>Inbox</AppSidebarItemLabel>
                <AppSidebarItemBadge>12</AppSidebarItemBadge>
              </AppSidebarItem>
              <AppSidebarSubmenu defaultOpen>
                <AppSidebarSubmenuTrigger label="Documents">
                  <AppSidebarItemIcon>
                    <FileText size={16} />
                  </AppSidebarItemIcon>
                  <AppSidebarItemLabel>Documents</AppSidebarItemLabel>
                </AppSidebarSubmenuTrigger>
                <AppSidebarSubmenuList>
                  <AppSidebarItem value="briefs" onClick={(e) => e.preventDefault()}>
                    <AppSidebarItemLabel>Project briefs</AppSidebarItemLabel>
                  </AppSidebarItem>
                  <AppSidebarItem value="contracts" onClick={(e) => e.preventDefault()}>
                    <AppSidebarItemLabel>Contracts</AppSidebarItemLabel>
                  </AppSidebarItem>
                </AppSidebarSubmenuList>
              </AppSidebarSubmenu>
              <AppSidebarItem value="team" label="Team" onClick={(e) => e.preventDefault()}>
                <AppSidebarItemIcon>
                  <Users size={16} />
                </AppSidebarItemIcon>
                <AppSidebarItemLabel>Team</AppSidebarItemLabel>
              </AppSidebarItem>
            </AppSidebarList>
          </AppSidebarGroup>
          <AppSidebarGroup>
            <AppSidebarGroupLabel>Account</AppSidebarGroupLabel>
            <AppSidebarList>
              <AppSidebarItem
                value="alerts"
                label="Notifications"
                onClick={(e) => e.preventDefault()}
              >
                <AppSidebarItemIcon>
                  <Bell size={16} />
                </AppSidebarItemIcon>
                <AppSidebarItemLabel>Notifications</AppSidebarItemLabel>
              </AppSidebarItem>
              <AppSidebarItem value="settings" label="Settings" onClick={(e) => e.preventDefault()}>
                <AppSidebarItemIcon>
                  <Settings size={16} />
                </AppSidebarItemIcon>
                <AppSidebarItemLabel>Settings</AppSidebarItemLabel>
              </AppSidebarItem>
            </AppSidebarList>
          </AppSidebarGroup>
        </AppSidebarNav>
        <AppSidebarFooter>
          <AppSidebarList>
            <AppSidebarItem value="help" label="Help center" onClick={(e) => e.preventDefault()}>
              <AppSidebarItemIcon>
                <CircleHelp size={16} />
              </AppSidebarItemIcon>
              <AppSidebarItemLabel>Help center</AppSidebarItemLabel>
            </AppSidebarItem>
          </AppSidebarList>
        </AppSidebarFooter>
      </AppSidebar>
      <section
        aria-live="polite"
        style={{
          display: "grid",
          alignContent: "start",
          gap: 6,
          flex: 1,
          minWidth: 0,
          padding: "20px 4px",
          fontSize: 13,
        }}
      >
        <span style={{ color: "var(--uai-subtle)", fontSize: 11.5 }}>Northwind Studio</span>
        <h3 style={{ margin: 0, fontSize: 18, lineHeight: "24px", fontWeight: 600 }}>
          {pages[page] ?? page}
        </h3>
        <p style={{ margin: 0, color: "var(--uai-muted)" }}>
          Pick a row to move the active pill. Collapse the panel to keep icons only.
        </p>
      </section>
    </div>
  );
}
```

## Anatomy

- `AppSidebar`
  - `AppSidebarHeader`
    - `AppSidebarTitle`
    - `AppSidebarCollapseToggle`
    - `AppSidebarMobileTrigger`
  - `AppSidebarNav`
    - `AppSidebarGroup`
      - `AppSidebarGroupLabel`
      - `AppSidebarList`
        - `AppSidebarItem`
          - `AppSidebarItemIcon`
          - `AppSidebarItemLabel`
          - `AppSidebarItemBadge`
        - `AppSidebarSubmenu`
          - `AppSidebarSubmenuTrigger`
          - `AppSidebarSubmenuList`
  - `AppSidebarFooter`

## Accessibility

- Rows are links with aria-current="page" on the active item; labels stay in the accessibility tree when the sidebar collapses.
- The collapse toggle and mobile trigger expose aria-expanded and aria-controls. Escape closes the mobile disclosure and returns focus to its trigger.
- Submenu triggers are disclosure buttons. In collapsed mode they expand the sidebar before opening the submenu.
- Routing, permissions, and persistence of the collapsed state remain consumer-owned.
