"use client";
import { AudioWaveform, BadgeCheck, Bell, BookOpen, Bot, ChevronRight, ChevronsUpDown, Command, CreditCard, Folder, Forward, Frame, LogOut, MapIcon, MoreHorizontal, PieChart, Plus, Settings2, Sparkles, SquareTerminal, Trash2,} from "lucide-react";import * as React from "react";
import { Avatar, AvatarFallback, AvatarImage,} from "@/components/ui/avatar";import { Collapsible, CollapsibleContent, CollapsibleTrigger,} from "@/components/ui/collapsible";import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger,} from "@/components/ui/dropdown-menu";import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarTrigger, useSidebar,} from "@/components/ui/sidebar";
const data = { user: { name: "Maqed", email: "[email protected]", avatar: "https://github.com/Maqed.png", }, teams: [ { name: "herocn", logo: HeroLogo, plan: "Enterprise", }, { name: "Acme Corp.", logo: AudioWaveform, plan: "Startup", }, { name: "Evil Corp.", logo: Command, plan: "Free", }, ], navMain: [ { title: "Playground", url: "#", icon: SquareTerminal, isActive: true, items: [ { title: "History", url: "#" }, { title: "Starred", url: "#" }, { title: "Settings", url: "#" }, ], }, { title: "Models", url: "#", icon: Bot, items: [ { title: "Genesis", url: "#" }, { title: "Explorer", url: "#" }, { title: "Quantum", url: "#" }, ], }, { title: "Documentation", url: "#", icon: BookOpen, items: [ { title: "Introduction", url: "#" }, { title: "Get Started", url: "#" }, { title: "Tutorials", url: "#" }, { title: "Changelog", url: "#" }, ], }, { title: "Settings", url: "#", icon: Settings2, items: [ { title: "General", url: "#" }, { title: "Team", url: "#" }, { title: "Billing", url: "#" }, { title: "Limits", url: "#" }, ], }, ], projects: [ { name: "Design Engineering", url: "#", icon: Frame }, { name: "Sales & Marketing", url: "#", icon: PieChart }, { name: "Travel", url: "#", icon: MapIcon }, ],};
function HeroLogo(props: React.SVGProps<SVGSVGElement>) { return ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144" width={144} height={144} fill="none" {...props} > <title>herocn Logo</title> <g stroke="currentColor" strokeWidth={16} strokeLinecap="round" strokeLinejoin="round" > <path d="M25 45 L25 80 L55 100 V65 L80 50 V135 L120 120 V35 L80 50 V10 L25 45 Z" /> <line x1={120} y1={80} x2={80} y2={125} /> <line x1={120} y1={45} x2={80} y2={90} /> </g> </svg> );}
function TeamSwitcher({ teams,}: { teams: { name: string; logo: React.ElementType; plan: string; }[];}) { const { isMobile } = useSidebar(); const [activeTeam, setActiveTeam] = React.useState(teams[0]);
if (!activeTeam) return null;
return ( <SidebarMenu> <SidebarMenuItem> <DropdownMenu> <DropdownMenuTrigger render={ <SidebarMenuButton size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground" /> } > <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"> <activeTeam.logo className="size-4" /> </div> <div className="grid flex-1 text-left text-sm leading-tight"> <span className="truncate font-medium">{activeTeam.name}</span> <span className="truncate text-xs">{activeTeam.plan}</span> </div> <ChevronsUpDown className="ml-auto" /> </DropdownMenuTrigger> <DropdownMenuContent className="min-w-56" align="start" side={isMobile ? "bottom" : "right"} sideOffset={4} > <DropdownMenuGroup> <DropdownMenuLabel className="text-muted-foreground text-xs"> Teams </DropdownMenuLabel> {teams.map((team, index) => ( <DropdownMenuItem key={team.name} onClick={() => setActiveTeam(team)} > <div className="flex size-6 items-center justify-center rounded-md border"> <team.logo className="size-3.5 shrink-0" /> </div> {team.name} <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut> </DropdownMenuItem> ))} </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> <div className="flex size-6 items-center justify-center rounded-md border"> <Plus className="size-4" /> </div> <div className="font-medium text-muted-foreground"> Add team </div> </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> </SidebarMenu> );}
function NavMain({ items,}: { items: { title: string; url: string; icon?: React.ElementType; isActive?: boolean; items?: { title: string; url: string }[]; }[];}) { return ( <SidebarGroup> <SidebarGroupLabel>Platform</SidebarGroupLabel> <SidebarMenu> {items.map((item) => ( <Collapsible key={item.title} defaultOpen={item.isActive}> <SidebarMenuItem> <CollapsibleTrigger render={<SidebarMenuButton tooltip={item.title} />} > {item.icon && <item.icon />} <span>{item.title}</span> <ChevronRight className="ml-auto transition-transform duration-200 group-data-panel-open/menu-button:rotate-90" /> </CollapsibleTrigger> <CollapsibleContent> <SidebarMenuSub> {item.items?.map((subItem) => ( <SidebarMenuSubItem key={subItem.title}> <SidebarMenuSubButton render={<a href={subItem.url} />}> <span>{subItem.title}</span> </SidebarMenuSubButton> </SidebarMenuSubItem> ))} </SidebarMenuSub> </CollapsibleContent> </SidebarMenuItem> </Collapsible> ))} </SidebarMenu> </SidebarGroup> );}
function NavProjects({ projects,}: { projects: { name: string; url: string; icon: React.ElementType; }[];}) { const { isMobile } = useSidebar();
return ( <SidebarGroup className="group-data-[collapsible=icon]:hidden"> <SidebarGroupLabel>Projects</SidebarGroupLabel> <SidebarMenu> {projects.map((item) => ( <SidebarMenuItem key={item.name}> <SidebarMenuButton render={<a href={item.url} />}> <item.icon /> <span>{item.name}</span> </SidebarMenuButton> <DropdownMenu> <DropdownMenuTrigger render={<SidebarMenuAction showOnHover />}> <MoreHorizontal /> <span className="sr-only">More</span> </DropdownMenuTrigger> <DropdownMenuContent className="w-48" side={isMobile ? "bottom" : "right"} align={isMobile ? "end" : "start"} > <DropdownMenuItem> <Folder className="text-muted-foreground" /> <span>View Project</span> </DropdownMenuItem> <DropdownMenuItem> <Forward className="text-muted-foreground" /> <span>Share Project</span> </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem> <Trash2 className="text-muted-foreground" /> <span>Delete Project</span> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> ))} <SidebarMenuItem> <SidebarMenuButton className="text-sidebar-foreground/70"> <MoreHorizontal className="text-sidebar-foreground/70" /> <span>More</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> );}
function NavUser({ user,}: { user: { name: string; email: string; avatar: string; };}) { const { isMobile } = useSidebar();
return ( <SidebarMenu> <SidebarMenuItem> <DropdownMenu> <DropdownMenuTrigger render={ <SidebarMenuButton size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground" /> } > <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> <ChevronsUpDown className="ml-auto size-4" /> </DropdownMenuTrigger> <DropdownMenuContent className="min-w-56" side={isMobile ? "bottom" : "right"} align="end" sideOffset={4} > <DropdownMenuGroup> <DropdownMenuLabel className="p-0 font-normal"> <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm"> <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> </DropdownMenuGroup> <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 /> <DropdownMenuGroup> <DropdownMenuItem> <LogOut /> Log out </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> </SidebarMenu> );}
export function SidebarDemo() { return ( <SidebarProvider> <Sidebar collapsible="icon"> <SidebarHeader> <TeamSwitcher teams={data.teams} /> </SidebarHeader> <SidebarContent> <NavMain items={data.navMain} /> <NavProjects projects={data.projects} /> </SidebarContent> <SidebarFooter> <NavUser user={data.user} /> </SidebarFooter> <SidebarRail /> </Sidebar> <SidebarInset> <header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12"> <div className="flex items-center gap-2 px-4"> <SidebarTrigger className="-ms-1" /> </div> </header> </SidebarInset> </SidebarProvider> );}pnpm dlx shadcn@latest add @herocn/sidebarimport { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"import { AppSidebar } from "@/components/app-sidebar"
export default function Layout({ children }: { children: React.ReactNode }) { return ( <SidebarProvider> <AppSidebar /> <main> <SidebarTrigger /> {children} </main> </SidebarProvider> )}import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarHeader,} from "@/components/ui/sidebar"
export function AppSidebar() { return ( <Sidebar> <SidebarHeader /> <SidebarContent> <SidebarGroup /> <SidebarGroup /> </SidebarContent> <SidebarFooter /> </Sidebar> )}Use the following composition to build a Sidebar layout:
SidebarProvider├── Sidebar│ ├── SidebarHeader│ ├── SidebarContent│ │ ├── SidebarGroup│ │ │ ├── SidebarGroupLabel│ │ │ ├── SidebarGroupAction│ │ │ ├── SidebarGroupContent│ │ │ └── SidebarMenu│ │ │ ├── SidebarMenuItem│ │ │ │ ├── SidebarMenuButton│ │ │ │ ├── SidebarMenuAction│ │ │ │ └── SidebarMenuBadge│ │ │ └── SidebarMenuItem│ │ │ ├── SidebarMenuButton│ │ │ └── SidebarMenuSub│ │ │ ├── SidebarMenuSubItem│ │ │ └── SidebarMenuSubItem│ │ └── SidebarGroup│ │ └── SidebarMenu│ │ ├── SidebarMenuItem│ │ └── SidebarMenuItem│ ├── SidebarFooter│ └── SidebarRail├── SidebarInset└── SidebarTriggerA floating sidebar is detached from the main content with rounded corners and a subtle shadow.
"use client";
import { BadgeCheck, Bell, ChevronsUpDown, CreditCard, Home, Inbox, LogOut, Search, Settings, 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 { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarRail, SidebarTrigger, useSidebar,} from "@/components/ui/sidebar";
function NavUser({ user,}: { user: { name: string; email: string; avatar: string; };}) { const { isMobile } = useSidebar();
return ( <SidebarMenu> <SidebarMenuItem> <DropdownMenu> <DropdownMenuTrigger render={ <SidebarMenuButton size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground" /> } > <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> <ChevronsUpDown className="ml-auto size-4" /> </DropdownMenuTrigger> <DropdownMenuContent className="min-w-56" side={isMobile ? "bottom" : "right"} align="end" sideOffset={4} > <DropdownMenuGroup> <DropdownMenuLabel className="p-0 font-normal"> <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm"> <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> </DropdownMenuGroup> <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 /> <DropdownMenuGroup> <DropdownMenuItem> <LogOut /> Log out </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> </SidebarMenu> );}
export function SidebarFloating() { return ( <SidebarProvider> <Sidebar variant="floating" collapsible="icon"> <SidebarHeader> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton size="lg" render={<a href="#" />}> <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144" width={144} height={144} fill="none" className="size-4" > <title>herocn Logo</title> <g stroke="currentColor" strokeWidth={16} strokeLinecap="round" strokeLinejoin="round" > <path d="M25 45 L25 80 L55 100 V65 L80 50 V135 L120 120 V35 L80 50 V10 L25 45 Z" /> <line x1={120} y1={80} x2={80} y2={125} /> <line x1={120} y1={45} x2={80} y2={90} /> </g> </svg> </div> <div className="grid flex-1 text-left text-sm leading-tight"> <span className="truncate font-medium">herocn</span> <span className="truncate text-xs">Enterprise</span> </div> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarHeader> <SidebarContent> <SidebarGroup> <SidebarGroupLabel>General</SidebarGroupLabel> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton render={<a href="#" />} isActive> <Home /> <span>Dashboard</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton render={<a href="#" />}> <Inbox /> <span>Inbox</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton render={<a href="#" />}> <Search /> <span>Search</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> <SidebarGroup> <SidebarGroupLabel>Account</SidebarGroupLabel> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton render={<a href="#" />}> <Settings /> <span>Settings</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton render={<a href="#" />}> <Bell /> <span>Notifications</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> </SidebarContent> <SidebarFooter> <NavUser user={{ name: "Maqed", email: "[email protected]", avatar: "https://github.com/Maqed.png", }} /> </SidebarFooter> <SidebarRail /> </Sidebar> <SidebarInset> <header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12"> <div className="flex items-center gap-2 px-4"> <SidebarTrigger className="-ms-1" /> </div> </header> </SidebarInset> </SidebarProvider> );}An inset sidebar sits inside the page with a bordered content area that shrinks when the sidebar collapses.
"use client";
import { BadgeCheck, Bell, Calendar, ChevronsUpDown, CreditCard, History, Home, LogOut, MapIcon, PieChart, Settings2, 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 { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset as SidebarInsetView, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, useSidebar,} from "@/components/ui/sidebar";
function NavUser({ user,}: { user: { name: string; email: string; avatar: string; };}) { const { isMobile } = useSidebar();
return ( <SidebarMenu> <SidebarMenuItem> <DropdownMenu> <DropdownMenuTrigger render={ <SidebarMenuButton size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground" /> } > <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> <ChevronsUpDown className="ml-auto size-4" /> </DropdownMenuTrigger> <DropdownMenuContent className="min-w-56" side={isMobile ? "bottom" : "right"} align="end" sideOffset={4} > <DropdownMenuGroup> <DropdownMenuLabel className="p-0 font-normal"> <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm"> <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> </DropdownMenuGroup> <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 /> <DropdownMenuGroup> <DropdownMenuItem> <LogOut /> Log out </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> </SidebarMenu> );}
export function SidebarInset() { return ( <SidebarProvider> <Sidebar variant="inset" collapsible="offcanvas"> <SidebarHeader> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground" > <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144" width={144} height={144} fill="none" className="size-4" > <title>herocn Logo</title> <g stroke="currentColor" strokeWidth={16} strokeLinecap="round" strokeLinejoin="round" > <path d="M25 45 L25 80 L55 100 V65 L80 50 V135 L120 120 V35 L80 50 V10 L25 45 Z" /> <line x1={120} y1={80} x2={80} y2={125} /> <line x1={120} y1={45} x2={80} y2={90} /> </g> </svg> </div> <div className="grid flex-1 text-left text-sm leading-tight"> <span className="truncate font-medium">herocn</span> <span className="truncate text-xs">Enterprise</span> </div> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarHeader> <SidebarContent> <SidebarGroup> <SidebarGroupLabel>Platform</SidebarGroupLabel> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton> <Home /> <span>Home</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <Calendar /> <span>Calendar</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <Settings2 /> <span>Settings</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> <SidebarGroup> <SidebarGroupLabel>Projects</SidebarGroupLabel> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton> <History /> <span>History</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <PieChart /> <span>Analytics</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <MapIcon /> <span>Travel</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> </SidebarContent> <SidebarFooter> <NavUser user={{ name: "Maqed", email: "[email protected]", avatar: "https://github.com/Maqed.png", }} /> </SidebarFooter> </Sidebar> <SidebarInsetView> <header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12"> <div className="flex items-center gap-2 px-4"> <SidebarTrigger className="-ms-1" /> </div> </header> <div className="flex flex-1 flex-col gap-4 p-4 pt-0"> <div className="grid auto-rows-min gap-4 md:grid-cols-3"> <div className="aspect-video rounded-xl bg-muted/50" /> <div className="aspect-video rounded-xl bg-muted/50" /> <div className="aspect-video rounded-xl bg-muted/50" /> </div> <div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" /> </div> </SidebarInsetView> </SidebarProvider> );}Use the collapsible="icon" prop to collapse the sidebar to icons. Pass a tooltip to SidebarMenuButton to show a tooltip when collapsed.
"use client";
import { BadgeCheck, Bell, Bookmark, ChevronsUpDown, Clock, CreditCard, HelpCircle, Home, Inbox, LogOut, Search, Settings, Sparkles, Star,} 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 { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarRail, SidebarTrigger, useSidebar,} from "@/components/ui/sidebar";
function NavUser({ user,}: { user: { name: string; email: string; avatar: string; };}) { const { isMobile } = useSidebar();
return ( <SidebarMenu> <SidebarMenuItem> <DropdownMenu> <DropdownMenuTrigger render={ <SidebarMenuButton size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground" /> } > <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> <ChevronsUpDown className="ml-auto size-4" /> </DropdownMenuTrigger> <DropdownMenuContent className="min-w-56" side={isMobile ? "bottom" : "right"} align="end" sideOffset={4} > <DropdownMenuGroup> <DropdownMenuLabel className="p-0 font-normal"> <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm"> <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> </DropdownMenuGroup> <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 /> <DropdownMenuGroup> <DropdownMenuItem> <LogOut /> Log out </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> </SidebarMenu> );}
export function SidebarCollapsible() { return ( <SidebarProvider> <Sidebar collapsible="icon"> <SidebarHeader> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton size="lg" tooltip="herocn"> <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144" width={144} height={144} fill="none" className="size-4" > <title>herocn Logo</title> <g stroke="currentColor" strokeWidth={16} strokeLinecap="round" strokeLinejoin="round" > <path d="M25 45 L25 80 L55 100 V65 L80 50 V135 L120 120 V35 L80 50 V10 L25 45 Z" /> <line x1={120} y1={80} x2={80} y2={125} /> <line x1={120} y1={45} x2={80} y2={90} /> </g> </svg> </div> <div className="grid flex-1 text-left text-sm leading-tight"> <span className="truncate font-medium">herocn</span> <span className="truncate text-xs">Enterprise</span> </div> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarHeader> <SidebarContent> <SidebarGroup> <SidebarGroupLabel>General</SidebarGroupLabel> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton isActive tooltip="Dashboard"> <Home /> <span>Dashboard</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton tooltip="Inbox"> <Inbox /> <span>Inbox</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton tooltip="Search"> <Search /> <span>Search</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> <SidebarGroup> <SidebarGroupLabel>Projects</SidebarGroupLabel> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton tooltip="Starred"> <Star /> <span>Starred</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton tooltip="Recent"> <Clock /> <span>Recent</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton tooltip="Bookmarks"> <Bookmark /> <span>Bookmarks</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> <SidebarGroup> <SidebarGroupLabel>Account</SidebarGroupLabel> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton tooltip="Settings"> <Settings /> <span>Settings</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton tooltip="Notifications"> <Bell /> <span>Notifications</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton tooltip="Help"> <HelpCircle /> <span>Help</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> </SidebarContent> <SidebarFooter> <NavUser user={{ name: "Maqed", email: "[email protected]", avatar: "https://github.com/Maqed.png", }} /> </SidebarFooter> <SidebarRail /> </Sidebar> <SidebarInset> <header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12"> <div className="flex items-center gap-2 px-4"> <SidebarTrigger className="-ms-1" /> </div> </header> </SidebarInset> </SidebarProvider> );}"use client";
import { AudioWaveform, BadgeCheck, Bell, BookOpen, Bot, ChevronRight, ChevronsUpDown, Command, CreditCard, Folder, Forward, Frame, LogOut, MapIcon, MoreHorizontal, PieChart, Plus, Settings2, Sparkles, SquareTerminal, Trash2,} from "lucide-react";import * as React from "react";
import { type Translations, useTranslation,} from "@/components/language-selector";import { Avatar, AvatarFallback, AvatarImage,} from "@/components/ui/avatar";import { Collapsible, CollapsibleContent, CollapsibleTrigger,} from "@/components/ui/collapsible";import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger,} from "@/components/ui/dropdown-menu";import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarTrigger, useSidebar,} from "@/components/ui/sidebar";
const translations: Translations = { en: { dir: "ltr", values: { addTeam: "Add team", teams: "Teams", platform: "Platform", projects: "Projects", viewProject: "View Project", shareProject: "Share Project", deleteProject: "Delete Project", more: "More", upgradeToPro: "Upgrade to Pro", account: "Account", billing: "Billing", notifications: "Notifications", logOut: "Log out", playground: "Playground", history: "History", starred: "Starred", settings: "Settings", models: "Models", genesis: "Genesis", explorer: "Explorer", quantum: "Quantum", documentation: "Documentation", introduction: "Introduction", getStarted: "Get Started", tutorials: "Tutorials", changelog: "Changelog", general: "General", team: "Team", limits: "Limits", designEngineering: "Design Engineering", salesMarketing: "Sales & Marketing", travel: "Travel", }, }, ar: { dir: "rtl", values: { addTeam: "إضافة فريق", teams: "الفرق", platform: "المنصة", projects: "المشاريع", viewProject: "عرض المشروع", shareProject: "مشاركة المشروع", deleteProject: "حذف المشروع", more: "المزيد", upgradeToPro: "ترقية إلى Pro", account: "الحساب", billing: "الفوترة", notifications: "الإشعارات", logOut: "تسجيل الخروج", playground: "ملعب", history: "السجل", starred: "المميز", settings: "الإعدادات", models: "النماذج", genesis: "جينيسيس", explorer: "إكسبلورر", quantum: "كوانتوم", documentation: "التوثيق", introduction: "مقدمة", getStarted: "ابدأ", tutorials: "الدروس", changelog: "سجل التغييرات", general: "عام", team: "الفريق", limits: "الحدود", designEngineering: "هندسة التصميم", salesMarketing: "المبيعات والتسويق", travel: "السفر", }, }, he: { dir: "rtl", values: { addTeam: "הוסף צוות", teams: "צוותים", platform: "פלטפורמה", projects: "פרויקטים", viewProject: "הצג פרויקט", shareProject: "שתף פרויקט", deleteProject: "מחק פרויקט", more: "עוד", upgradeToPro: "שדרג ל-Pro", account: "חשבון", billing: "חיוב", notifications: "התראות", logOut: "התנתק", playground: "מגרש משחקים", history: "היסטוריה", starred: "מועדפים", settings: "הגדרות", models: "מודלים", genesis: "ג'נסיס", explorer: "אקספלורר", quantum: "קוונטום", documentation: "תיעוד", introduction: "מבוא", getStarted: "התחל", tutorials: "מדריכים", changelog: "יומן שינויים", general: "כללי", team: "צוות", limits: "מגבלות", designEngineering: "הנדסת עיצוב", salesMarketing: "מכירות ושיווק", travel: "נסיעות", }, },};
export function SidebarRtl() { const { dir, language, t } = useTranslation(translations, "ar");
const teams = [ { name: "herocn", logo: HeroLogo, plan: "Enterprise" }, { name: "Acme Corp.", logo: AudioWaveform, plan: "Startup" }, { name: "Evil Corp.", logo: Command, plan: "Free" }, ];
const navMain = [ { title: t.playground, url: "#", icon: SquareTerminal, isActive: true, items: [ { title: t.history, url: "#" }, { title: t.starred, url: "#" }, { title: t.settings, url: "#" }, ], }, { title: t.models, url: "#", icon: Bot, items: [ { title: t.genesis, url: "#" }, { title: t.explorer, url: "#" }, { title: t.quantum, url: "#" }, ], }, { title: t.documentation, url: "#", icon: BookOpen, items: [ { title: t.introduction, url: "#" }, { title: t.getStarted, url: "#" }, { title: t.tutorials, url: "#" }, { title: t.changelog, url: "#" }, ], }, { title: t.settings, url: "#", icon: Settings2, items: [ { title: t.general, url: "#" }, { title: t.team, url: "#" }, { title: t.billing, url: "#" }, { title: t.limits, url: "#" }, ], }, ];
const projects = [ { name: t.designEngineering, url: "#", icon: Frame }, { name: t.salesMarketing, url: "#", icon: PieChart }, { name: t.travel, url: "#", icon: MapIcon }, ];
const user = { name: "Maqed", email: "[email protected]", avatar: "https://github.com/Maqed.png", };
return ( <SidebarProvider lang={language} dir={dir}> <Sidebar collapsible="icon" dir={dir} side={dir === "ltr" ? "left" : "right"} > <SidebarHeader> <TeamSwitcher teams={teams} dir={dir} t={t} /> </SidebarHeader> <SidebarContent> <NavMain items={navMain} t={t} /> <NavProjects projects={projects} dir={dir} t={t} /> </SidebarContent> <SidebarFooter> <NavUser user={user} dir={dir} t={t} /> </SidebarFooter> <SidebarRail /> </Sidebar> <SidebarInset> <header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12"> <div className="flex items-center gap-2 px-4"> <SidebarTrigger className="-ms-1" /> </div> </header> </SidebarInset> </SidebarProvider> );}
function HeroLogo(props: React.SVGProps<SVGSVGElement>) { return ( <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144 144" width={144} height={144} fill="none" {...props} > <title>herocn Logo</title> <g stroke="currentColor" strokeWidth={16} strokeLinecap="round" strokeLinejoin="round" > <path d="M25 45 L25 80 L55 100 V65 L80 50 V135 L120 120 V35 L80 50 V10 L25 45 Z" /> <line x1={120} y1={80} x2={80} y2={125} /> <line x1={120} y1={45} x2={80} y2={90} /> </g> </svg> );}
function TeamSwitcher({ teams, dir, t,}: { teams: { name: string; logo: React.ElementType; plan: string }[]; dir: "ltr" | "rtl"; t: typeof translations.en.values;}) { const { isMobile } = useSidebar(); const [activeTeam, setActiveTeam] = React.useState(teams[0]);
if (!activeTeam) return null;
return ( <SidebarMenu> <SidebarMenuItem> <DropdownMenu> <DropdownMenuTrigger render={ <SidebarMenuButton size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground" /> } > <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"> <activeTeam.logo className="size-4" /> </div> <div className="grid flex-1 text-left text-sm leading-tight"> <span className="truncate font-medium">{activeTeam.name}</span> <span className="truncate text-xs">{activeTeam.plan}</span> </div> <ChevronsUpDown className="ms-auto" /> </DropdownMenuTrigger> <DropdownMenuContent className="min-w-56" align="start" side={isMobile ? "bottom" : "inline-end"} sideOffset={4} dir={dir} > <DropdownMenuGroup> <DropdownMenuLabel className="text-muted-foreground text-xs"> {t.teams} </DropdownMenuLabel> {teams.map((team, index) => ( <DropdownMenuItem key={team.name} onClick={() => setActiveTeam(team)} > <div className="flex size-6 items-center justify-center rounded-md border"> <team.logo className="size-3.5 shrink-0" /> </div> {team.name} <DropdownMenuShortcut>⌘{index + 1}</DropdownMenuShortcut> </DropdownMenuItem> ))} </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuItem> <div className="flex size-6 items-center justify-center rounded-md border"> <Plus className="size-4" /> </div> <div className="font-medium text-muted-foreground"> {t.addTeam} </div> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> </SidebarMenu> );}
function NavMain({ items, t,}: { items: { title: string; url: string; icon?: React.ElementType; isActive?: boolean; items?: { title: string; url: string }[]; }[]; t: typeof translations.en.values;}) { return ( <SidebarGroup> <SidebarGroupLabel>{t.platform}</SidebarGroupLabel> <SidebarMenu> {items.map((item) => ( <Collapsible key={item.title} defaultOpen={item.isActive}> <SidebarMenuItem> <CollapsibleTrigger render={<SidebarMenuButton />}> {item.icon && <item.icon />} <span>{item.title}</span> <ChevronRight className="ms-auto transition-transform duration-200 group-data-open/collapsible:rotate-90 rtl:rotate-180" /> </CollapsibleTrigger> <CollapsibleContent> <SidebarMenuSub> {item.items?.map((subItem) => ( <SidebarMenuSubItem key={subItem.title}> <SidebarMenuSubButton render={<a href={subItem.url} />}> <span>{subItem.title}</span> </SidebarMenuSubButton> </SidebarMenuSubItem> ))} </SidebarMenuSub> </CollapsibleContent> </SidebarMenuItem> </Collapsible> ))} </SidebarMenu> </SidebarGroup> );}
function NavProjects({ projects, dir, t,}: { projects: { name: string; url: string; icon: React.ElementType }[]; dir: "ltr" | "rtl"; t: typeof translations.en.values;}) { const { isMobile } = useSidebar();
return ( <SidebarGroup className="group-data-[collapsible=icon]:hidden"> <SidebarGroupLabel>{t.projects}</SidebarGroupLabel> <SidebarMenu> {projects.map((item) => ( <SidebarMenuItem key={item.name}> <SidebarMenuButton render={<a href={item.url} />}> <item.icon /> <span>{item.name}</span> </SidebarMenuButton> <DropdownMenu> <DropdownMenuTrigger render={<SidebarMenuAction showOnHover />}> <MoreHorizontal /> <span className="sr-only">{t.more}</span> </DropdownMenuTrigger> <DropdownMenuContent className="w-48" side={isMobile ? "bottom" : "inline-end"} align={isMobile ? "end" : "start"} dir={dir} > <DropdownMenuItem> <Folder className="text-muted-foreground" /> <span>{t.viewProject}</span> </DropdownMenuItem> <DropdownMenuItem> <Forward className="text-muted-foreground" /> <span>{t.shareProject}</span> </DropdownMenuItem> <DropdownMenuSeparator /> <DropdownMenuItem> <Trash2 className="text-muted-foreground" /> <span>{t.deleteProject}</span> </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> ))} <SidebarMenuItem> <SidebarMenuButton className="text-sidebar-foreground/70"> <MoreHorizontal className="text-sidebar-foreground/70" /> <span>{t.more}</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroup> );}
function NavUser({ user, dir, t,}: { user: { name: string; email: string; avatar: string }; dir: "ltr" | "rtl"; t: typeof translations.en.values;}) { const { isMobile } = useSidebar();
return ( <SidebarMenu> <SidebarMenuItem> <DropdownMenu> <DropdownMenuTrigger render={ <SidebarMenuButton size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground" /> } > <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> <ChevronsUpDown className="ms-auto size-4" /> </DropdownMenuTrigger> <DropdownMenuContent className="min-w-56 rounded-lg" side={isMobile ? "bottom" : "inline-end"} align="end" sideOffset={4} dir={dir} > <DropdownMenuGroup> <DropdownMenuLabel className="p-0 font-normal"> <div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm"> <Avatar className="size-8 rounded-lg"> <AvatarImage src={user.avatar} alt={user.name} /> <AvatarFallback className="rounded-lg">Mqd</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> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> <Sparkles /> {t.upgradeToPro} </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> <BadgeCheck /> {t.account} </DropdownMenuItem> <DropdownMenuItem> <CreditCard /> {t.billing} </DropdownMenuItem> <DropdownMenuItem> <Bell /> {t.notifications} </DropdownMenuItem> </DropdownMenuGroup> <DropdownMenuSeparator /> <DropdownMenuGroup> <DropdownMenuItem> <LogOut /> {t.logOut} </DropdownMenuItem> </DropdownMenuGroup> </DropdownMenuContent> </DropdownMenu> </SidebarMenuItem> </SidebarMenu> );}| Prop | Type | Default |
|---|---|---|
| defaultOpen | boolean | true |
Default open state of the sidebar. | ||
| open | boolean | — |
Open state of the sidebar (controlled). | ||
| onOpenChange | (open: boolean) => void | — |
Sets the open state of the sidebar (controlled). | ||
| Prop | Type | Default |
|---|---|---|
| side | "left" | "right" | "left" |
The side of the screen the sidebar appears on. | ||
| variant | "sidebar" | "floating" | "inset" | "sidebar" |
Controls the visual style of the sidebar. | ||
| collapsible | "offcanvas" | "icon" | "none" | "offcanvas" |
Controls how the sidebar collapses. | ||
| dir | "ltr" | "rtl" | — |
Sets the text direction for RTL support. | ||
| Prop | Type | Default |
|---|---|---|
| state | "expanded" | "collapsed" | — |
The current state of the sidebar. | ||
| open | boolean | — |
Whether the sidebar is open on desktop. | ||
| setOpen | (open: boolean) => void | — |
Sets the open state of the sidebar on desktop. | ||
| openMobile | boolean | — |
Whether the sidebar is open on mobile. | ||
| setOpenMobile | (open: boolean) => void | — |
Sets the open state of the sidebar on mobile. | ||
| isMobile | boolean | — |
Whether the viewport is in mobile mode. | ||
| toggleSidebar | () => void | — |
Toggles the sidebar on both desktop and mobile. | ||
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "outline" | "default" |
The visual variant of the menu button. | ||
| size | "default" | "sm" | "lg" | "default" |
The size of the menu button. | ||
| isActive | boolean | false |
Marks the menu button as active. | ||
| tooltip | string | TooltipContentProps | — |
Shows a tooltip when the sidebar is collapsed to icons. Pass a string for simple text or an object for custom content. | ||
| Prop | Type | Default |
|---|---|---|
| showOnHover | boolean | false |
Only show the action on hover or focus. | ||
| Prop | Type | Default |
|---|---|---|
| size | "sm" | "md" | "md" |
The size of the sub button. | ||
| isActive | boolean | false |
Marks the sub button as active. | ||
| Prop | Type | Default |
|---|---|---|
| showIcon | boolean | false |
Shows a placeholder icon alongside the text skeleton. | ||