lumiui
blocksidebar-02source: ok · hit
Sidebar 02
Dashboard layout with sidebar and main content area.
npx shadcn add "https://www.lumiui.dev/r/sidebar-02.json"free
Where it goes
sidebardashboard-shellkanban
Use cases
dashboard
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lightRegistry dependencies
@lumi-ui/sidebar@lumi-ui/separator@lumi-ui/scroll-area@lumi-ui/dropdown-menu@lumi-ui/chart@lumi-ui/collapsible
Files4 · source cached 10/14/2026, 3:12:55 PM
registry/blocks/sidebar-02/page.tsx
import { cookies } from "next/headers";
import type { Layout } from "react-resizable-panels";
import { AppSidebar } from "@/registry/blocks/sidebar-02/components/app-sidebar";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/registry/ui/breadcrumb";
import { Separator } from "@/registry/ui/separator";
import {
MainContent,
SidebarProvider,
SidebarTrigger,
} from "@/registry/ui/sidebar";
const GROUP_ID = "main-layout-persistence";
export default async function Page() {
const cookieStore = await cookies();
const layoutCookie = cookieStore.get(GROUP_ID);
const defaultLayout: Layout = layoutCookie
? JSON.parse(layoutCookie.value)
: undefined;
return (
<SidebarProvider
collapsibleType="sidebar"
defaultLayout={defaultLayout}
groupId={GROUP_ID}
>
<AppSidebar />
<MainContent className="flex flex-col">
<header className="flex h-12 bg-background z-10 sticky top-0 items-center gap-2 p-2 sm:p-4">
<SidebarTrigger />
<Separator className="mr-2 h-4" orientation="vertical" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">Dashboard</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Financial Performance</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</header>
<div className="flex flex-1 flex-col gap-4 p-4">
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="bg-muted/50 aspect-video rounded-xl" />
<div className="bg-muted/50 aspect-video rounded-xl" />
<div className="bg-muted/50 aspect-video rounded-xl" />
</div>
<div className="bg-muted/50 min-h-[100vh] flex-1 rounded-xl md:min-h-min" />
</div>
</MainContent>
</SidebarProvider>
);
}
registry/blocks/sidebar-02/components/app-sidebar.tsx
import { ScrollArea } from "@/registry/ui/scroll-area";
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "@/registry/ui/sidebar";
import { NavMain } from "./nav-main";
import { VersionSwitcher } from "./version-switcher";
export function AppSidebar() {
return (
<Sidebar>
<SidebarHeader>
<VersionSwitcher
defaultVersion={data.versions[0]}
versions={data.versions}
/>
</SidebarHeader>
<SidebarContent>
<ScrollArea gradientScrollFade>
<NavMain items={data.navMain} />
{data.navMain.map((item) => (
<SidebarGroup key={item.title}>
<SidebarGroupLabel>{item.title}</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{item.items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton
isActive={item.isActive}
render={<a href={item.url}>{item.title}</a>}
tooltip={{
children: "hello",
}}
/>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
))}
</ScrollArea>
</SidebarContent>
</Sidebar>
);
}
const data = {
navMain: [
{
items: [
{
title: "Overview",
url: "#",
},
{
isActive: true,
title: "Analytics",
url: "#",
},
{
title: "Reports",
url: "#",
},
],
title: "Dashboard",
url: "#",
},
{
items: [
{
title: "All Users",
url: "#",
},
{
title: "Roles & Permissions",
url: "#",
},
{
title: "Teams",
url: "#",
},
{
title: "Invitations",
url: "#",
},
{
title: "Activity Logs",
url: "#",
},
],
title: "User Management",
url: "#",
},
{
items: [
{
title: "All Projects",
url: "#",
},
{
title: "Active Projects",
url: "#",
},
{
title: "Archived",
url: "#",
},
{
title: "Templates",
url: "#",
},
],
title: "Projects",
url: "#",
},
{
items: [
{
title: "Transactions",
url: "#",
},
{
title: "Subscriptions",
url: "#",
},
{
title: "Invoices",
url: "#",
},
{
title: "Billing",
url: "#",
},
{
title: "Payment Methods",
url: "#",
},
],
title: "Sales & Revenue",
url: "#",
},
{
items: [
{
title: "General",
url: "#",
},
{
title: "Security",
url: "#",
},
{
title: "Integrations",
url: "#",
},
{
title: "Notifications",
url: "#",
},
{
title: "API Keys",
url: "#",
},
{
title: "Webhooks",
url: "#",
},
],
title: "Settings",
url: "#",
},
],
user: {
email: "m@example.com",
name: "Lumi UI",
},
versions: ["1.0.1", "1.1.0-alpha", "2.0.0-beta1"],
};
registry/blocks/sidebar-02/components/nav-main.tsx
"use client";
import { ChevronRight, type LucideIcon } from "lucide-react";
import {
Collapsible,
CollapsiblePanel,
CollapsibleTrigger,
} from "@/registry/ui/collapsible";
import {
SidebarGroup,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "@/registry/ui/sidebar";
export function NavMain({
items,
}: {
items: {
title: string;
url: string;
icon?: LucideIcon;
isActive?: boolean;
items?: {
title: string;
url: string;
}[];
}[];
}) {
return (
<SidebarGroup>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<Collapsible defaultOpen={item.isActive}>
<CollapsibleTrigger
className="group/collapsible data-[panel-open]:bg-sidebar-accent data-[panel-open]:text-sidebar-accent-foreground"
render={
<SidebarMenuButton
tooltip={{
children: item.title,
side: "right",
}}
>
{item.icon && <item.icon />}
<span>{item.title}</span>
<ChevronRight className="ml-auto text-muted-foreground transition-[transform,colors] duration-200 group-data-[panel-open]/collapsible:rotate-90 group-data-[panel-open]/collapsible:text-foreground group-hover/collapsible:text-sidebar-foreground" />
</SidebarMenuButton>
}
/>
<CollapsiblePanel>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton
render={
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
}
/>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsiblePanel>
</Collapsible>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroup>
);
}
registry/blocks/sidebar-02/components/version-switcher.tsx
"use client";
import {
BadgeCheckIcon,
ChevronsUpDown,
GalleryVerticalEnd,
} from "lucide-react";
import * as React from "react";
import {
DropdownMenu,
DropdownMenuCheckboxItemContent,
DropdownMenuContent,
DropdownMenuTrigger,
} from "@/registry/ui/dropdown-menu";
import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "@/registry/ui/sidebar";
export function VersionSwitcher({
versions,
defaultVersion,
}: {
versions: string[];
defaultVersion: string;
}) {
const [selectedVersion, setSelectedVersion] = React.useState(defaultVersion);
return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger
render={
<SidebarMenuButton
className="data-[popup-open]:bg-sidebar-accent data-[popup-open]:text-sidebar-accent-foreground justify-between px-2 gap-2"
size="lg"
>
<span>
<GalleryVerticalEnd className="size-4" />
</span>
<div className="flex flex-col gap-1 leading-none">
<span className="font-medium">Documentation</span>
<span className="text-muted-foreground">
v{selectedVersion}
</span>
</div>
<ChevronsUpDown className="ml-auto" />
</SidebarMenuButton>
}
/>
<DropdownMenuContent matchAnchorWidth>
{versions.map((version) => (
<DropdownMenuCheckboxItemContent
checked={version === selectedVersion}
closeOnClick
indicatorIcon={<BadgeCheckIcon />}
indicatorPlacement="end"
key={version}
onCheckedChange={() => setSelectedVersion(version)}
>
v{version}
</DropdownMenuCheckboxItemContent>
))}
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
);
}