UI Kit/Sidebar 1

Sidebar 1

App navigation with groups, nested items and a collapsed icon rail. This website uses it.

Acme Corp / Home
View code
"use client";

import {
  Activity,
  Blocks,
  Calendar,
  CreditCard,
  FolderKanban,
  Globe,
  Inbox,
  LayoutGrid,
  LogOut,
  Settings,
  Terminal,
  Users,
} from "lucide-react";
import { useState } from "react";

import {
  Sidebar1,
  Sidebar1Brand,
  Sidebar1Collapsible,
  Sidebar1Content,
  Sidebar1Footer,
  Sidebar1Group,
  Sidebar1Header,
  Sidebar1Item,
  Sidebar1Search,
  Sidebar1Toggle,
} from "@/components/kit/sidebar-1";

export function Sidebar1Demo() {
  const [collapsed, setCollapsed] = useState(false);
  const [page, setPage] = useState("Home");
  const item = (name: string, icon: React.ReactNode, badge?: number) => (
    <Sidebar1Item icon={icon} badge={badge} active={page === name} onClick={() => setPage(name)}>
      {name}
    </Sidebar1Item>
  );
  const subItem = (name: string) => (
    <Sidebar1Item active={page === name} onClick={() => setPage(name)}>
      {name}
    </Sidebar1Item>
  );

  return (
    <div className="flex h-180 w-full bg-background text-foreground">
      <Sidebar1 collapsed={collapsed}>
        <Sidebar1Header>
          <Sidebar1Brand logo="A" title="Acme Corp" subtitle="Pro Plan" onClick={() => setPage("Home")} />
        </Sidebar1Header>
        <Sidebar1Content aria-label="Main">
          <Sidebar1Search />
          {item("Home", <LayoutGrid />)}
          {item("Inbox", <Inbox />, 12)}
          {item("Analytics", <Activity />)}
          <Sidebar1Group label="Workspace">
            <Sidebar1Collapsible icon={<FolderKanban />} label="Projects">
              {subItem("Active")}
              {subItem("Archived")}
            </Sidebar1Collapsible>
            {item("Calendar", <Calendar />)}
            <Sidebar1Collapsible icon={<Users />} label="Team">
              {subItem("Members")}
              {subItem("Roles")}
            </Sidebar1Collapsible>
            <Sidebar1Collapsible icon={<Globe />} label="Customers">
              {subItem("Accounts")}
              {subItem("Invoices")}
            </Sidebar1Collapsible>
            {item("Finance", <CreditCard />)}
          </Sidebar1Group>
          <Sidebar1Group label="Developers">
            {item("API Keys", <Terminal />)}
            {item("Webhooks", <Blocks />)}
          </Sidebar1Group>
        </Sidebar1Content>
        <Sidebar1Footer>
          {item("Settings", <Settings />)}
          <Sidebar1Item icon={<LogOut />}>Log out</Sidebar1Item>
        </Sidebar1Footer>
      </Sidebar1>

      <div className="flex min-w-0 flex-1 flex-col max-sm:hidden">
        <header className="flex h-14 shrink-0 items-center gap-3 border-b px-3">
          <Sidebar1Toggle collapsed={collapsed} onClick={() => setCollapsed((value) => !value)} />
          <span className="truncate text-sm text-muted-foreground">
            Acme Corp <span className="mx-1">/</span> <span className="font-medium text-foreground">{page}</span>
          </span>
        </header>
        <div className="flex flex-1 flex-col gap-5 overflow-hidden p-6" aria-hidden="true">
          <div className="h-8 w-48 rounded-lg bg-muted" />
          <div className="grid h-32 shrink-0 grid-cols-2 gap-5">
            <div className="rounded-xl border bg-card" />
            <div className="rounded-xl border bg-card" />
          </div>
          <div className="flex flex-1 flex-col gap-3 rounded-xl border bg-card p-5">
            <div className="h-5 w-40 rounded-md bg-muted" />
            <div className="h-11 rounded-lg bg-muted" />
            <div className="h-11 rounded-lg bg-muted" />
            <div className="h-11 rounded-lg bg-muted" />
          </div>
        </div>
      </div>
    </div>
  );
}

Build your navigation from small parts. You hold the collapsed state, so you can save it or change it from anywhere.

Installation

bunx --bun shadcn@latest add HarshitMahajan/UI/sidebar-1

Copies the code to components/kit/sidebar-1.tsx along with button, which it uses, installs its packages, and adds the tokens it needs to your globals.css. Your existing shadcn components aren't touched.

Usage

import {
  Sidebar1,
  Sidebar1Brand,
  Sidebar1Collapsible,
  Sidebar1Content,
  Sidebar1Footer,
  Sidebar1Group,
  Sidebar1Header,
  Sidebar1Item,
  Sidebar1Search,
  Sidebar1Toggle,
} from "@/components/kit/sidebar-1"
<Sidebar1 collapsed={collapsed}>
  <Sidebar1Header>
    <Sidebar1Brand logo="A" title="Acme Corp" subtitle="Pro Plan" />
  </Sidebar1Header>
  <Sidebar1Content aria-label="Main">
    <Sidebar1Search />
    <Sidebar1Item icon={<LayoutGrid />} active>Home</Sidebar1Item>
    <Sidebar1Group label="Workspace">
      <Sidebar1Collapsible icon={<Users />} label="Team">
        <Sidebar1Item>Members</Sidebar1Item>
      </Sidebar1Collapsible>
    </Sidebar1Group>
  </Sidebar1Content>
  <Sidebar1Footer>
    <Sidebar1Item icon={<Settings />}>Settings</Sidebar1Item>
  </Sidebar1Footer>
</Sidebar1>

Composition

Sidebar1
├── Sidebar1Header
│   └── Sidebar1Brand
├── Sidebar1Content
│   ├── Sidebar1Search
│   ├── Sidebar1Item
│   └── Sidebar1Group
│       ├── Sidebar1Item
│       └── Sidebar1Collapsible
│           └── Sidebar1Item
└── Sidebar1Footer
    └── Sidebar1Item

Sidebar1Toggle (usually in your page header)

Features

  • Collapses to a 60px icon rail over 200ms
  • Groups, nested rows and count badges
  • Items render as buttons or as links through the render prop
  • Nested rows use Base UI Collapsible, and search uses Base UI Input

Icon rail

Set collapsed to show only icons. Give each item an aria-label, since its text is out of view.

View code
import { Activity, Inbox, LayoutGrid, Settings } from "lucide-react";

import { Sidebar1, Sidebar1Brand, Sidebar1Content, Sidebar1Footer, Sidebar1Header, Sidebar1Item } from "@/components/kit/sidebar-1";

export function Sidebar1Rail() {
  return (
    <div className="flex h-80 w-full bg-background text-foreground">
      <Sidebar1 collapsed>
        <Sidebar1Header>
          <Sidebar1Brand logo="A" title="Acme Corp" />
        </Sidebar1Header>
        <Sidebar1Content aria-label="Main">
          <Sidebar1Item icon={<LayoutGrid />} active aria-label="Home">
            Home
          </Sidebar1Item>
          <Sidebar1Item icon={<Inbox />} badge={12} aria-label="Inbox, 12 new">
            Inbox
          </Sidebar1Item>
          <Sidebar1Item icon={<Activity />} aria-label="Analytics">
            Analytics
          </Sidebar1Item>
        </Sidebar1Content>
        <Sidebar1Footer>
          <Sidebar1Item icon={<Settings />} aria-label="Settings">
            Settings
          </Sidebar1Item>
        </Sidebar1Footer>
      </Sidebar1>
    </div>
  );
}

Nested items

Use Sidebar1Collapsible for sections with sub-items, and defaultOpen to start one open.

View code
import { FolderKanban, Users } from "lucide-react";

import { Sidebar1, Sidebar1Collapsible, Sidebar1Content, Sidebar1Item } from "@/components/kit/sidebar-1";

export function Sidebar1Nested() {
  return (
    <div className="flex h-80 w-full bg-background text-foreground">
      <Sidebar1>
        <Sidebar1Content aria-label="Main">
          <Sidebar1Collapsible icon={<FolderKanban />} label="Projects" defaultOpen>
            <Sidebar1Item active>Active</Sidebar1Item>
            <Sidebar1Item>Archived</Sidebar1Item>
          </Sidebar1Collapsible>
          <Sidebar1Collapsible icon={<Users />} label="Team">
            <Sidebar1Item>Members</Sidebar1Item>
            <Sidebar1Item>Roles</Sidebar1Item>
          </Sidebar1Collapsible>
        </Sidebar1Content>
      </Sidebar1>
    </div>
  );
}

With links

Pass render to make items real links. In Next.js, pass your Link component.

View code
import { Inbox, LayoutGrid } from "lucide-react";

import { Sidebar1, Sidebar1Content, Sidebar1Group, Sidebar1Item } from "@/components/kit/sidebar-1";

// In a Next.js app, pass render={<Link href="/inbox" />} to get client-side navigation.
export function Sidebar1Links() {
  return (
    <div className="flex h-60 w-full bg-background text-foreground">
      <Sidebar1>
        <Sidebar1Content aria-label="Main">
          <Sidebar1Group label="Pages" className="mt-0">
            <Sidebar1Item render={<a href="#install" />} icon={<LayoutGrid />} active>
              Home
            </Sidebar1Item>
            <Sidebar1Item render={<a href="#install" />} icon={<Inbox />} badge={3}>
              Inbox
            </Sidebar1Item>
          </Sidebar1Group>
        </Sidebar1Content>
      </Sidebar1>
    </div>
  );
}

Accessibility

Name the navigation

Sidebar1Content renders a nav element. Give it an aria-label so people can tell navigation areas apart.

The current page

Set active on the current item. It's marked with aria-current, so screen readers announce it too.

API Reference

Sidebar1

The frame.

PropTypeDefaultDescription
collapsedbooleanfalseSwitches to the icon rail.
classNamestring-Additional classes for the frame.

Sidebar1Toggle

The collapse button. Accepts all Button props.

PropTypeDefaultDescription
collapsedboolean-The current state, which sets its icon and label.

Sidebar1Brand

Your logo and name.

PropTypeDefaultDescription
logoReactNode-A letter or an image.
titlestring-The name.
subtitlestring-A line under the name.
onClick() => void-Makes it a button with a chevron, such as a workspace switcher.

Sidebar1Item

One row.

PropTypeDefaultDescription
iconReactNode-The row's icon.
activebooleanfalseMarks the current page.
badgeReactNode-A short count on the right. Hidden on the rail.
renderReactElement | function-Render the row as another element, such as a link or your router's Link.

Sidebar1Collapsible

A row that opens to show sub-items. It closes on the rail.

PropTypeDefaultDescription
labelstring-The row's text.
iconReactNode-The row's icon.
defaultOpenbooleanfalseWhether it starts open.

Sidebar1Group

A labeled set of rows.

PropTypeDefaultDescription
labelstring-The group's name. It fades on the rail.

Sidebar1Search

A filter field styled as a row. Accepts all Base UI Input props.

Sidebar1Header

The top area.

Sidebar1Content

The scrolling middle. Renders a nav element.

Sidebar1Footer

The bottom area, above a line.