Skip to Content

sidebar-16

block

Full-featured sidebar combining all patterns.

Source Code

View the full component source code below.

"use client"

import { useState } from "react"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Separator } from "@/components/ui/separator"
import {
  ChevronDown,
  ChevronsUpDown,
  Check,
  Home,
  BarChart3,
  Users,
  FileText,
  Settings,
  Inbox,
  Bell,
  Plus,
  Search,
  Moon,
  Sun,
  LogOut,
  HelpCircle,
} from "lucide-react"

const workspaces = [
  { id: "mukoko", label: "mukoko", color: "bg-cobalt" },
  { id: "nhimbe", label: "nhimbe", color: "bg-tanzanite" },
]

const navGroups = [
  {
    label: "Main",
    items: [
      { icon: Home, label: "Home" },
      { icon: BarChart3, label: "Analytics" },
      { icon: Inbox, label: "Inbox", count: 5 },
      { icon: Bell, label: "Notifications", count: 2 },
    ],
  },
  {
    label: "Workspace",
    items: [
      { icon: Users, label: "Team" },
      { icon: FileText, label: "Documents" },
    ],
  },
]

const quickActions = [
  { icon: Plus, label: "New project", color: "text-cobalt" },
]

function Sidebar16() {
  const [activeWs, setActiveWs] = useState("mukoko")
  const [wsOpen, setWsOpen] = useState(false)
  const [dark, setDark] = useState(false)
  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({})

  const toggleGroup = (label: string) =>
    setCollapsed((prev) => ({ ...prev, [label]: !prev[label] }))

  return (
    <aside className="flex h-screen w-64 flex-col border-r border-border bg-card">
      {/* Workspace switcher */}
      <div className="relative p-3">
        <button
          onClick={() => setWsOpen(!wsOpen)}
          className="flex w-full items-center gap-3 rounded-lg border border-border px-3 py-2 text-left hover:bg-muted"
        >
          <span className={`size-5 shrink-0 rounded ${workspaces.find((w) => w.id === activeWs)?.color}`} />
          <span className="flex-1 text-sm font-medium text-foreground">
            {workspaces.find((w) => w.id === activeWs)?.label}
          </span>
          <ChevronsUpDown className="size-4 text-muted-foreground" />
        </button>
        {wsOpen && (
          <div className="absolute inset-x-3 top-full z-10 mt-1 rounded-lg border border-border bg-popover p-1 shadow-md">
            {workspaces.map((ws) => (
              <button
                key={ws.id}
                onClick={() => { setActiveWs(ws.id); setWsOpen(false) }}
                className="flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm hover:bg-muted"
              >
                <span className={`size-4 shrink-0 rounded ${ws.color}`} />
                <span className="flex-1 text-foreground">{ws.label}</span>
                {activeWs === ws.id && <Check className="size-4 text-malachite" />}
              </button>
            ))}
          </div>
        )}
      </div>

      {/* Search */}
      <div className="px-3 pb-2">
        <div className="relative">
          <Search className="absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
          <Input placeholder="Search..." className="h-8 pl-8 text-sm" />
        </div>
      </div>

      {/* Quick actions */}
      <div className="px-3 pb-2">
        {quickActions.map((a) => (
          <Button key={a.label} variant="outline" className="w-full justify-start gap-3" size="sm">
            <a.icon className={`size-4 ${a.color}`} />
            {a.label}
          </Button>
        ))}
      </div>

      <Separator />

      {/* Grouped nav */}
      <nav className="flex-1 overflow-auto p-3">
        {navGroups.map((group) => (
          <div key={group.label} className="mb-2">
            <button
              onClick={() => toggleGroup(group.label)}
              className="flex w-full items-center justify-between px-3 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground"
            >
              {group.label}
              <ChevronDown className={`size-3 transition-transform ${collapsed[group.label] ? "-rotate-90" : ""}`} />
            </button>
            {!collapsed[group.label] && (
              <div className="mt-0.5 space-y-0.5">
                {group.items.map((item) => (
                  <Button
                    key={item.label}
                    variant="ghost"
                    className="w-full justify-start gap-3"
                    size="sm"
                  >
                    <item.icon className="size-4" />
                    <span className="flex-1 text-left">{item.label}</span>
                    {item.count && <Badge variant="secondary">{item.count}</Badge>}
                  </Button>
                ))}
              </div>
            )}
          </div>
        ))}
      </nav>

      <Separator />

      {/* Theme + help */}
      <div className="space-y-1 p-3">
        <div className="flex items-center justify-between rounded-lg bg-muted px-3 py-1.5">
          <span className="text-xs text-muted-foreground">Theme</span>
          <button
            onClick={() => setDark(!dark)}
            className="flex size-7 items-center justify-center rounded-md bg-background text-foreground hover:bg-accent"
            aria-label="Toggle theme"
          >
            {dark ? <Sun className="size-3.5" /> : <Moon className="size-3.5" />}
          </button>
        </div>
        <Button variant="ghost" className="w-full justify-start gap-3 text-muted-foreground" size="sm">
          <HelpCircle className="size-4" />
          Help & Support
        </Button>
        <Button variant="ghost" className="w-full justify-start gap-3 text-muted-foreground" size="sm">
          <Settings className="size-4" />
          Settings
        </Button>
      </div>

      <Separator />

      {/* User footer */}
      <div className="flex items-center gap-3 p-4">
        <Avatar size="sm">
          <AvatarFallback>TM</AvatarFallback>
        </Avatar>
        <div className="flex-1 overflow-hidden">
          <p className="truncate text-sm font-medium text-foreground">Tanya Moyo</p>
          <p className="truncate text-xs text-muted-foreground">tanya@nyuchi.com</p>
        </div>
        <Button variant="ghost" size="icon-sm">
          <LogOut className="size-4" />
        </Button>
      </div>
    </aside>
  )
}

export { Sidebar16 }

Installation

npx shadcn@latest add https://registry.mukoko.com/api/v1/ui/sidebar-16

Dependencies

API

Fetch this component's metadata and source code from the registry API.

GET/api/v1/ui/sidebar-16

Source

components/blocks/sidebar-16.tsx