All Taste components
Navigation
Sidebar navigation
An app sidebar with grouped sections, the current page marked, counts, and a collapsed icon rail.
expanded · icon rail · sections · counts · account footer
import { BarChart3, Calendar, ChevronsUpDown, CreditCard, FileText, Home, Settings, Users, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
type Item = { href: string; label: string; icon: LucideIcon; count?: number };
type Section = { label: string; items: Item[] };
const sections: Section[] = [
{
label: "Workspace",
items: [
{ href: "/", label: "Dashboard", icon: Home },
{ href: "/invoices", label: "Invoices", icon: FileText, count: 12 },
{ href: "/customers", label: "Customers", icon: Users },
{ href: "/bookings", label: "Bookings", icon: Calendar, count: 3 },
{ href: "/reports", label: "Reports", icon: BarChart3 },
],
},
{
label: "Account",
items: [
{ href: "/billing", label: "Billing", icon: CreditCard },
{ href: "/settings", label: "Settings", icon: Settings },
],
},
];
export function Sidebar({ current, collapsed = false }: { current: string; collapsed?: boolean }) {
return (
<nav
aria-label="Workspace"
className={cn(
"flex min-h-full flex-col gap-4 rounded-lg border border-border bg-surface",
collapsed ? "w-14 items-center px-2 py-3" : "w-[232px] p-3",
)}
>
{collapsed ? (
<span className="mb-3 grid size-7 place-items-center rounded-sm bg-primary text-xs font-semibold text-primary-fg">K</span>
) : (
<button type="button" className="flex w-full items-center gap-2 rounded p-1.5 text-left text-[13.5px] font-medium text-fg hover:bg-surface-2">
<span className="grid size-[22px] place-items-center rounded-sm bg-primary text-xs font-semibold text-primary-fg">K</span>
Kaveri Studio
<ChevronsUpDown className="ml-auto size-3.5 text-fg-subtle" aria-hidden />
</button>
)}
{sections.map((section) => (
<div key={section.label} className="grid gap-0.5">
{!collapsed && <p className="mb-1 px-2 text-[11.5px] font-medium text-fg-subtle">{section.label}</p>}
{section.items.map(({ href, label, icon: Icon, count }) => (
<a
key={href}
href={href}
title={collapsed ? label : undefined}
aria-label={collapsed ? (count ? label + ", " + count : label) : undefined}
aria-current={current === href ? "page" : undefined}
className={cn(
"group relative flex h-8 items-center gap-2.5 rounded px-2 text-[13.5px] font-medium text-fg-muted transition",
"hover:bg-surface-2 hover:text-fg",
"aria-[current=page]:bg-bg aria-[current=page]:text-fg aria-[current=page]:shadow-sm aria-[current=page]:ring-1 aria-[current=page]:ring-border",
collapsed && "size-9 justify-center px-0",
)}
>
<Icon className="size-4 shrink-0" aria-hidden />
{!collapsed && label}
{count !== undefined && (
<span
className={cn(
"grid place-items-center rounded-full font-medium tabular-nums",
collapsed
? "absolute -right-px top-px h-4 min-w-4 bg-primary px-1 text-[10px] text-primary-fg ring-2 ring-surface"
: "ml-auto h-[18px] min-w-5 bg-surface-2 px-1.5 text-[11.5px] text-fg-muted group-aria-[current=page]:bg-primary-soft group-aria-[current=page]:text-primary-soft-fg",
)}
>
{count}
</span>
)}
</a>
))}
</div>
))}
</nav>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.