# App Sidebars

> Four app-shell sidebars — simple with search and count badges, sectioned with nested disclosures, a dual-tier icon rail with a swapping panel, and one that collapses to a rail — with gliding active pills, height and width springs and blurred panel swaps.

- Collection: Blocks
- Tier: pro
- Docs: https://geturui.com/docs/app-sidebars
- Install: `npx shadcn@latest add @geturui-pro/app-sidebars`
- Dependencies: motion, lucide-react

## Variants

### Simple

Logo, a search field that filters the nav live, icon items with count badges, footer links and an account menu that opens upward.

Install: `npx shadcn@latest add @geturui-pro/app-sidebars-simple`

```tsx
import { SidebarSimple } from "@/components/blocks/app-sidebars/simple";
import { Inbox, LayoutDashboard } from "lucide-react";

export function Nav() {
  const [page, setPage] = React.useState("overview");
  return (
    <SidebarSimple
      brand="Acme"
      value={page}
      onValueChange={setPage}
      items={[
        { id: "overview", label: "Overview", icon: LayoutDashboard, href: "/" },
        { id: "inbox", label: "Inbox", icon: Inbox, href: "/inbox", count: 12 },
      ]}
      onAccountAction={(a) => a === "sign-out" && signOut()}
    />
  );
}
```

| Prop | Type | Default | Description |
|---|---|---|---|
| brand | `string` | "Kestrel" | Name beside the mark. |
| items / footerItems | `{ id; label; icon: LucideIcon; href?; count? }[]` | — | Main and footer items. href renders a link. |
| value / defaultValue | `string` | "overview" | Active page id (controlled / uncontrolled). |
| onValueChange | `(id: string) => void` | — | Called when a page is chosen. |
| account | `{ name; email }` | — | Account row in the footer. |
| onAccountAction | `(action: "profile" | "billing" | "sign-out") => void` | — | Account menu choice. |
| label | `string` | "Main" | Accessible name of the nav. |
| className | `string` | — | Classes for the outer element. |

### Sectioned

Group headings, nested items that spring open with a rotating chevron, and an active pill that glides between top-level and child pages via shared layout.

Install: `npx shadcn@latest add @geturui-pro/app-sidebars-sectioned`

```tsx
import { SidebarSectioned } from "@/components/blocks/app-sidebars/sectioned";
import { Workflow } from "lucide-react";

<SidebarSectioned
  brand="Relay"
  groups={[{ label: "Workspace", items: [{ id: "pipes", label: "Pipelines", icon: Workflow, children: [{ id: "runs", label: "Runs" }] }] }]}
  defaultValue="runs"
/>
```

| Prop | Type | Default | Description |
|---|---|---|---|
| brand / context | `string` | — | Name and a status subline (for example the environment). |
| groups | `{ label; items: { id; label; icon; href?; badge?; children?: { id; label; href? }[] }[] }[]` | — | Headed groups. Items with children become disclosures. |
| value / defaultValue | `string` | "pipelines-runs" | Active page id (controlled / uncontrolled). |
| onValueChange | `(id: string) => void` | — | Called when a page is chosen. |
| defaultExpanded | `string[]` | — | Disclosures open on mount; the active page’s parent always opens. |
| usage | `{ used: number; limit: number }` | — | Footer meter that fills on a spring. |
| className | `string` | — | Classes for the outer element. |

### Dual-tier

A narrow icon rail beside a wide panel; picking a rail section glides its indicator and swaps the panel content with a blurred crossfade.

Install: `npx shadcn@latest add @geturui-pro/app-sidebars-dual-tier`

```tsx
import { SidebarDualTier } from "@/components/blocks/app-sidebars/dual-tier";

<SidebarDualTier defaultSection="projects" onValueChange={(id) => router.push(`/p/${id}`)} />
```

| Prop | Type | Default | Description |
|---|---|---|---|
| sections | `{ id; label; icon; description?; groups: { label?; links: { id; label; href?; count? }[] }[] }[]` | — | Rail sections and the panel content for each. |
| section / defaultSection / onSectionChange | `string / string / (id) => void` | "projects" | Section shown in the panel. |
| value / defaultValue | `string` | "proj-website" | Active link id (controlled / uncontrolled). |
| onValueChange | `(id: string) => void` | — | Called when a link is chosen. |
| accountInitials | `string` | "JT" | Avatar at the bottom of the rail. |
| className | `string` | — | Classes for the outer element. |

### Collapsible

Collapses to a 68px icon rail: the width springs, labels fade, a gliding pill marks the page and rail items get side tooltips. Persists nothing.

Install: `npx shadcn@latest add @geturui-pro/app-sidebars-collapsible`

```tsx
import { SidebarCollapsible } from "@/components/blocks/app-sidebars/collapsible";

export function Shell() {
  const [collapsed, setCollapsed] = React.useState(false);
  return <SidebarCollapsible collapsed={collapsed} onCollapsedChange={setCollapsed} />;
}
```

| Prop | Type | Default | Description |
|---|---|---|---|
| items | `{ id; label; icon; href?; count? }[]` | — | Main items. Counts become a dot on the rail. |
| projects / projectsLabel | `{ id; label; tone?: "volt" | "ember" | "info" | "success" | "warning"; href? }[] / string` | — | Second group with coloured markers. |
| collapsed / defaultCollapsed / onCollapsedChange | `boolean / boolean / (c) => void` | false | Rail state. Nothing is stored. |
| value / defaultValue | `string` | "dashboard" | Active page id (controlled / uncontrolled). |
| onValueChange | `(id: string) => void` | — | Called when a page is chosen. |
| account | `{ name; role }` | — | Footer account row. |
| className | `string` | — | Classes for the outer element. |

## Accessibility

- Each sidebar is a nav landmark; the current page carries aria-current="page" and icons are aria-hidden.
- Disclosures and the collapse toggle are buttons with aria-expanded and aria-controls; the dual-tier rail is a vertical tablist with arrow, Home and End keys.
- The account menu is a role=menu with arrow keys and Escape back to its trigger; rail tooltips follow focus as well as hover.
- Height, width and panel springs fall back to instant changes under reduced motion.

## Source

Pro item — send `Authorization: Bearer <registry token>` to include the source.
