bundui
componentpage-layouts-02source: ok · hit
Page Layouts
Design organized and intuitive dashboard experiences with Tailwind CSS page layouts. These sections combine sidebars, headers, content areas, and responsive grids to present data clearly. Perfect for SaaS platforms, admin panels, and analytics tools seeking modern, user-friendly, and visually structured interfaces.
npx shadcn add "https://bundui.io/r/page-layouts-02.json"free
Where it goes
navbarsidebardashboard-shellkanban
Use cases
dashboard
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lighticonslucide-react
Registry dependencies
sidebarseparatorbuttonbadgecollapsibleavatarbuttonsidebar
Files8 · source cached 10/8/2026, 3:40:57 PM
examples/blocks/dashboard-ui/page-layouts/02/components/app-header.tsx
import * as React from "react";
import { SidebarTrigger } from "@/components/ui/sidebar";
import { Separator } from "@/components/ui/separator";
import { NavMainHorizontal } from "./nav-main-horizontal";
import { navigationLinks } from "./app-sidebar";
import { NavUser } from "./nav-user";
import { Logo } from "./logo";
import { BellIcon, MailIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
export function AppHeader() {
return (
<header
data-slot="header"
className="flex h-16 shrink-0 items-center justify-between gap-2 px-4 [&_[data-logo=description]]:hidden">
<div className="flex grow items-center">
<Logo />
<Separator orientation="vertical" className="mx-4 data-[orientation=vertical]:h-4" />
<SidebarTrigger className="-ml-1 md:hidden" />
<NavMainHorizontal items={navigationLinks} />
</div>
<div className="flex items-center gap-6">
<div className="flex items-center gap-2">
<Button size="icon-sm" variant="ghost">
<MailIcon />
</Button>
<Button size="icon-sm" variant="ghost" className="relative">
<BellIcon />
<Badge className="absolute -top-2 left-full min-w-5 -translate-x-1/2 px-1 text-[10px]">
5
</Badge>
</Button>
</div>
<NavUser />
</div>
</header>
);
}
examples/blocks/dashboard-ui/page-layouts/02/components/app-sidebar.tsx
"use client";
import * as React from "react";
import { BookOpenIcon, InfoIcon, LifeBuoyIcon } from "lucide-react";
import {
Sidebar,
SidebarContent,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem
} from "@/components/ui/sidebar";
import { NavMainVertical } from "./nav-main-vertical";
import { useIsMobile } from "@/hooks/use-mobile";
import { Logo } from "./logo";
export const navigationLinks = [
{
title: "Get Started",
type: "description",
items: [
{
url: "#",
title: "Dashboard",
description: "Browse all components in the library."
},
{
url: "#",
title: "Ecommerce",
description: "Learn how to use the library."
},
{
url: "#",
title: "Reports",
description: "Pre-built layouts for common use cases."
}
]
},
{
title: "Apps",
type: "simple",
items: [
{ url: "#", title: "Chats" },
{ url: "#", title: "Kanban Board" },
{ url: "#", title: "Notes" },
{ url: "#", title: "Event Calendar" }
]
},
{
title: "Pages",
type: "icon",
items: [
{ url: "#", title: "Profile Page", icon: BookOpenIcon },
{ url: "#", title: "User List", icon: LifeBuoyIcon },
{ url: "#", title: "Pricing Page", icon: InfoIcon }
]
}
];
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
const isMobile = useIsMobile();
if (!isMobile) return null;
return (
<Sidebar variant="inset" {...props}>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild>
<Logo />
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<NavMainVertical items={navigationLinks} />
</SidebarContent>
</Sidebar>
);
}
examples/blocks/dashboard-ui/page-layouts/02/components/logo.tsx
import * as React from "react";
import Link from "next/link";
export function Logo() {
return (
<Link href="#" className="flex gap-3">
<img src="https://shadcnuikit.com/logo.png" className="size-8 rounded-md" alt="" />
<div
data-logo="description"
className="grid flex-1 text-left text-sm leading-tight md:hidden">
<span className="truncate font-medium">Acme Inc</span>
<span className="truncate text-xs">Enterprise</span>
</div>
</Link>
);
}
examples/blocks/dashboard-ui/page-layouts/02/components/nav-main-horizontal.tsx
"use client";
import { type LucideIcon } from "lucide-react";
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger
} from "@/components/ui/navigation-menu";
import { cn } from "@/lib/utils";
export function NavMainHorizontal({
items
}: {
items: {
title: string;
type: string;
url?: string;
icon?: LucideIcon;
items?: {
title: string;
url: string;
description?: string;
icon?: LucideIcon;
}[];
}[];
}) {
return (
<NavigationMenu viewport={false} className="max-md:hidden">
<NavigationMenuList className="gap-2">
{items.map((link, index) => (
<NavigationMenuItem key={index}>
{link.items?.length ? (
<>
<NavigationMenuTrigger className="text-muted-foreground hover:text-primary bg-transparent px-2 py-1.5 font-medium *:[svg]:-me-0.5 *:[svg]:size-3.5">
{link.title}
</NavigationMenuTrigger>
<NavigationMenuContent className="data-[motion=from-end]:slide-in-from-right-16! data-[motion=from-start]:slide-in-from-left-16! data-[motion=to-end]:slide-out-to-right-16! data-[motion=to-start]:slide-out-to-left-16! z-50 p-1">
<ul className={cn(link.type === "description" ? "min-w-64" : "min-w-48")}>
{link.items.map((item, itemIndex) => (
<li key={itemIndex}>
<NavigationMenuLink href={item.url} className="py-1.5">
{/* Display icon if present */}
{link.type === "icon" && "icon" in item && (
<div className="flex items-center gap-2">
{item.icon && (
<item.icon
size={16}
className="text-foreground opacity-60"
aria-hidden="true"
/>
)}
<span>{item.title}</span>
</div>
)}
{/* Display label with description if present */}
{link.type === "description" && "description" in item ? (
<div className="space-y-1">
<div className="font-medium">{item.title}</div>
<p className="text-muted-foreground line-clamp-2 text-xs">
{item.description}
</p>
</div>
) : (
// Display simple label if not icon or description type
!link.type ||
(link.type !== "icon" && link.type !== "description" && (
<span>{item.title}</span>
))
)}
</NavigationMenuLink>
</li>
))}
</ul>
</NavigationMenuContent>
</>
) : (
<NavigationMenuLink
href={link.url}
className="text-muted-foreground hover:text-primary py-1.5 font-medium">
{link.title}
</NavigationMenuLink>
)}
</NavigationMenuItem>
))}
</NavigationMenuList>
</NavigationMenu>
);
}
examples/blocks/dashboard-ui/page-layouts/02/components/nav-main-vertical.tsx
"use client";
import { ChevronRight, type LucideIcon } from "lucide-react";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuAction,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem
} from "@/components/ui/sidebar";
export function NavMainVertical({
items
}: {
items: {
title: string;
type: string;
url?: string;
icon?: LucideIcon;
items?: {
title: string;
url: string;
description?: string;
icon?: LucideIcon;
}[];
}[];
}) {
return (
<>
{items.map((item, i) => (
<SidebarGroup>
<SidebarGroupLabel>{item.title}</SidebarGroupLabel>
<SidebarMenu>
{item.items?.length ? (
<>
{item.items?.map((subItem) => (
<SidebarMenuItem key={subItem.title}>
<SidebarMenuButton asChild>
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</>
) : null}
</SidebarMenu>
</SidebarGroup>
))}
</>
);
}
examples/blocks/dashboard-ui/page-layouts/02/components/nav-projects.tsx
"use client";
import { type LucideIcon } from "lucide-react";
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem
} from "@/components/ui/sidebar";
import Link from "next/link";
export function NavProjects({
projects
}: {
projects: {
name: string;
url: string;
icon: LucideIcon;
}[];
}) {
return (
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarMenu>
{projects.map((item) => (
<SidebarMenuItem key={item.name}>
<SidebarMenuButton asChild>
<Link href={item.url}>
<item.icon />
<span>{item.name}</span>
</Link>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroup>
);
}
examples/blocks/dashboard-ui/page-layouts/02/components/nav-user.tsx
"use client";
import { BadgeCheck, Bell, CreditCard, LogOut, Sparkles } from "lucide-react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
const user = {
name: "shadcn",
email: "m@example.com",
avatar: "https://ui.shadcn.com/avatars/shadcn.jpg"
};
export function NavUser() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="p-0">
<Avatar>
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="min-w-56 rounded-lg" align="end" sideOffset={4}>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar>
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Sparkles />
Upgrade to Pro
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard />
Billing
</DropdownMenuItem>
<DropdownMenuItem>
<Bell />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
examples/blocks/dashboard-ui/page-layouts/02/page.tsx
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { AppSidebar } from "./components/app-sidebar";
import { AppHeader } from "./components/app-header";
export default function Page() {
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset className="bg-muted/50">
<AppHeader />
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
<div className="bg-background grid min-h-[100vh] flex-1 place-items-center rounded-xl md:min-h-min">
<span className="text-muted-foreground text-sm">Content goes here</span>
</div>
</div>
</SidebarInset>
</SidebarProvider>
);
}