All components

Page Layouts 03

Page Layouts 03 is a React and Tailwind CSS component from Bund UI, licensed MIT. Copy it and paste it into your project.

Bund UI MIT other

What it is

Page Layouts 03 is a React and Tailwind CSS component from Bund UI, licensed MIT. Copy it and paste it into your project.

How to use it

Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.

Open in the catalogue

Five free copies a month. No card.

Needs

npm i lucide-react

Licence

This component comes from Bund UI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.

Page Layouts 03

examples/blocks/dashboard-ui/page-layouts/03/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 ThemeToggle from "./theme-toggle";

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">
          <ThemeToggle />
          <Button size="icon-sm" variant="ghost" className="relative">
            <MailIcon />
            <span className="bg-foreground absolute end-0 top-0 inline size-1.5 rounded-full" />
          </Button>
          <Button size="icon-sm" variant="ghost" className="relative">
            <BellIcon />
            <span className="bg-foreground absolute end-0 top-0 inline size-1.5 rounded-full" />
          </Button>
        </div>
        <NavUser />
      </div>
    </header>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/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",
    isActive: true,
    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/03/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/03/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;
    isActive?: boolean;
    items?: {
      title: string;
      url: string;
      description?: string;
      icon?: LucideIcon;
      isActive?: boolean;
    }[];
  }[];
}) {
  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 border-y-2 border-y-transparent bg-transparent px-2 py-1.5 font-medium *:[svg]:-me-0.5 *:[svg]:size-3.5">
                  {link.title}
                  {link.isActive && (
                    <div className="bg-primary absolute start-2 end-2 -bottom-4 h-0.5"></div>
                  )}
                </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/03/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/03/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/03/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/03/components/theme-toggle.tsx
"use client";

import { useState } from "react";
import { MoonIcon, SunIcon } from "lucide-react";

import { Toggle } from "@/components/ui/toggle";

export default function ThemeToggle() {
  const [theme, setTheme] = useState<string>("light");

  return (
    <div>
      <Toggle
        variant="outline"
        className="group text-muted-foreground data-[state=on]:text-muted-foreground data-[state=on]:hover:bg-muted data-[state=on]:hover:text-foreground size-8 rounded-full border-none shadow-none data-[state=on]:bg-transparent"
        pressed={theme === "dark"}
        onPressedChange={() => setTheme((prev) => (prev === "dark" ? "light" : "dark"))}
        aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}>
        {/* Note: After dark mode implementation, rely on dark: prefix rather than group-data-[state=on]: */}
        <MoonIcon
          size={16}
          className="shrink-0 scale-0 opacity-0 transition-all group-data-[state=on]:scale-100 group-data-[state=on]:opacity-100"
          aria-hidden="true"
        />
        <SunIcon
          size={16}
          className="absolute shrink-0 scale-100 opacity-100 transition-all group-data-[state=on]:scale-0 group-data-[state=on]:opacity-0"
          aria-hidden="true"
        />
      </Toggle>
    </div>
  );
}
examples/blocks/dashboard-ui/page-layouts/03/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>
  );
}

Similar components