# Rail

> The app's left rail, 199px wide: your app's name, Search, places grouped with their counts, one pinned at the bottom, and the workspace you're in.

- Name: `rail`
- Status: beta
- Categories: navigation
- File: the install writes `rail.tsx` to your `ui` alias, `@/components/ui` by default.
- npm packages: `@base-ui/react`, `lucide-react`. The install adds them.
- Other registry items: `@arbokui/badge`, `@arbokui/icon`, `@arbokui/kbd`, `@arbokui/team-flag`, `@arbokui/tooltip`. The install adds them.
- Gallery: https://arbokui.com/components/rail
- Registry item: https://arbokui.com/r/rail.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/rail
   ```

## Use it for

- The left edge of an app with a handful of places: Home, Messages, each team, Projects.
- Group places with a title, like "Teams". Give a place a badge for its count, in tone "warning" when it needs someone, or "brand" for news.
- Give each place an href to make it a link, or leave it off and handle onSelect. Pass active for the place you're in.
- Pass onSearch to show Search at the top. ⌘K (Ctrl+K) opens it from anywhere but a text field. If the page has a Command Menu, give that shortcut={false}, so one ⌘K opens one search.
- Put Settings in footer, so it stays at the bottom however many places there are. Put the workspace you're in, with its colors, in club.
- The rail fills its parent's height. Give the parent a height, or pass a className like h-dvh.

## Avoid it for

- Phones. A 199px rail leaves too little room. Use a bottom bar or a sheet there.
- Places inside one page. Use tabs.
- Switching workspaces. Put a switcher in header.

## Props

- `groups`: `{ title?: string; items: RailItem[] }[]`. Required. RailItem is { id, label, icon?, href?, badge?, tone? }. icon is a name from Icon's set or any icon. tone is "neutral", "warning", or "brand".
- `active`: `string`. The id of the place you're in. Controlled, with onSelect.
- `defaultActive`: `string`. The place you start in, when active is not set.
- `onSelect`: `(id: string) => void`. Runs with the item's id when you press it.
- `onSearch`: `() => void`. Opens search. Shows Search at the top and listens for ⌘K (Ctrl+K).
- `label`: `string`. Default `"Main"`. The navigation's name for screen readers.
- `header`: `ReactNode`. The top of the rail, like your app's name or a switcher.
- `footer`: `RailItem`. One item pinned at the bottom, like Settings.
- `club`: `{ label: string; colors?: [string, string]; pattern?: "hoops" | "halves" | "thirds" | "split" }`. The workspace you're in, at the foot, with its colors as a TeamFlag.
- `alerts`: `number`. Its open alerts, as a count beside its name.
- `className`: `string`. Extra classes on the rail. Later classes win.

## Accessibility

- The rail is a navigation landmark named "Main", or label. Each group is a list named by its title.
- Places are links, or buttons without an href. Each is a Tab stop, top to bottom. The place you're in says aria-current="page".
- The place you're in is a raised card and bold. Forced-colors mode draws an outline around it.
- Search says its shortcut in aria-keyshortcuts. ⌘K never fires while you type in a field.
- The wash follows the pointer and the keyboard. It never shows for touch, and it moves at once with reduced motion.
- Headings, Search, and icons hold 4.5:1 on the rail's gray and under the wash, in both themes.

## Examples

### Grouped places, Search, and a pinned footer

```tsx
<div className="flex h-[580px]">
  <Rail
    header={<span className="font-semibold">Your company</span>}
    groups={[
      { items: [{ id: "home", label: "Home", icon: "house", href: "/" }, { id: "messages", label: "Messages", icon: "message-circle", href: "/messages", badge: 3, tone: "brand" }] },
      { title: "Teams", items: [{ id: "design", label: "Design", icon: "users", href: "/teams/design" }] },
    ]}
    active="home"
    onSearch={() => console.log("search")}
    footer={{ id: "settings", label: "Settings", icon: "settings", href: "/settings" }}
    club={{ label: "Your team", colors: ["#1e3a8a", "#ffffff"], pattern: "hoops" }}
    alerts={2}
  />
</div>
```
