Sidebar
A composable, themeable and customizable sidebar component.
'use client'
import { Avatar, AvatarFallback, AvatarImage } from '@nerdfish/react/avatar'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@nerdfish/react/breadcrumb'
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from '@nerdfish/react/collapsible'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuTrigger,
} from '@nerdfish/react/dropdown-menu'
import { Separator } from '@nerdfish/react/separator'
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuAction,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarTrigger,
} from '@nerdfish/react/sidebar'
import {
AudioWaveform,
BadgeCheck,
Bell,
BookOpen,
Bot,
ChevronRight,
ChevronsUpDown,
Command,
CreditCard,
Folder,
Forward,
Frame,
GalleryVerticalEnd,
LogOut,
Map,
MoreHorizontal,
PieChart,
Plus,
Settings2,
Sparkles,
SquareTerminal,
Trash2,
} from 'lucide-react'
import * as React from 'react'
export const iframeHeight = '800px'
export const description = 'A sidebar that collapses to icons.'
// This is sample data.
const data = {
user: {
name: 'shadcn',
email: 'm@example.com',
avatar: '/avatars/shadcn.jpg',
},
teams: [
{
name: 'Acme Inc',
logo: GalleryVerticalEnd,
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: Map,
},
],
}
export default function SidebarExample() {
const [activeTeam, setActiveTeam] = React.useState(data.teams[0])
if (!activeTeam) {
return null
}
return (
<div className="p-best-friends border-border rounded-base relative h-fit translate-0 transform overflow-hidden overflow-y-auto border **:max-h-125!">
<SidebarProvider className="min-h-125!">
<Sidebar collapsible="icon">
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger
render={
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-muted data-[state=open]:text-sidebar-muted-contrast"
>
<div className="bg-sidebar-secondary text-sidebar-secondary-contrast rounded-compact flex aspect-square size-8 shrink-0 items-center justify-center">
<activeTeam.logo className="size-4" />
</div>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{activeTeam.name}
</span>
<span className="truncate text-xs">
{activeTeam.plan}
</span>
</div>
<ChevronsUpDown className="ml-auto" />
</SidebarMenuButton>
}
/>
<DropdownMenuContent
className="rounded-compact w-[--radix-dropdown-menu-trigger-width] min-w-56"
align="start"
side="right"
sideOffset={4}
>
<DropdownMenuGroup>
<DropdownMenuLabel className="text-muted-contrast text-xs">
Teams
</DropdownMenuLabel>
{data.teams.map((team, index) => (
<DropdownMenuItem
key={team.name}
onClick={() => setActiveTeam(team)}
className="gap-best-friends p-2"
>
<div className="flex size-6 items-center justify-center rounded-sm border">
<team.logo className="size-4 shrink-0" />
</div>
{team.name}
<DropdownMenuShortcut>
⌘{index + 1}
</DropdownMenuShortcut>
</DropdownMenuItem>
))}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem className="gap-best-friends p-2">
<div className="bg-background flex size-6 items-center justify-center rounded-md border">
<Plus className="text-foreground size-4" />
</div>
<div className="font-medium">Add team</div>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarMenu>
{data.navMain.map((item) => (
<Collapsible
key={item.title}
render={
<SidebarMenuItem>
<CollapsibleTrigger
render={
<SidebarMenuButton tooltip={item.title}>
<item.icon />
<span>{item.title}</span>
<ChevronRight className="duration-fast ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
}
/>
<CollapsibleContent>
<SidebarMenuSub>
{item.items.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton
render={
<a href={subItem.url}>
<span>{subItem.title}</span>
</a>
}
/>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
}
defaultOpen={item.isActive}
className="group/collapsible"
/>
))}
</SidebarMenu>
</SidebarGroup>
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarMenu>
{data.projects.map((item) => (
<SidebarMenuItem key={item.name}>
<SidebarMenuButton
tooltip="View Project"
className="text-sidebar-foreground"
render={<a href={item.url} />}
>
<item.icon />
<span>{item.name}</span>
</SidebarMenuButton>
<DropdownMenu>
<DropdownMenuTrigger
render={
<SidebarMenuAction showOnHover>
<MoreHorizontal />
<span className="sr-only">More</span>
</SidebarMenuAction>
}
/>
<DropdownMenuContent
className="rounded-compact w-48"
side="bottom"
align="end"
>
<DropdownMenuGroup>
<DropdownMenuItem>
<Folder className="text-muted-contrast" />
<span>View Project</span>
</DropdownMenuItem>
<DropdownMenuItem>
<Forward className="text-muted-contrast" />
<span>Share Project</span>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>
<Trash2 className="text-muted-contrast" />
<span>Delete Project</span>
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
))}
<SidebarMenuItem>
<SidebarMenuButton className="text-sidebar-foreground/70">
<MoreHorizontal className="text-sidebar-foreground/70" />
<span>More</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger
render={
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-muted data-[state=open]:text-sidebar-muted-contrast"
>
<Avatar className="rounded-compact h-8 w-8">
<AvatarImage
src={data.user.avatar}
alt={data.user.name}
/>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{data.user.name}
</span>
<span className="truncate text-xs">
{data.user.email}
</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
}
/>
<DropdownMenuContent
className="rounded-compact w-[--radix-dropdown-menu-trigger-width] min-w-56"
side="bottom"
align="end"
sideOffset={4}
>
<DropdownMenuGroup>
<DropdownMenuLabel className="p-0 font-normal">
<div className="gap-best-friends px-bff py-bff flex items-center text-left text-sm">
<Avatar className="rounded-compact h-8 w-8">
<AvatarImage
src={data.user.avatar}
alt={data.user.name}
/>
<AvatarFallback className="rounded-compact">
CN
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{data.user.name}
</span>
<span className="truncate text-xs">
{data.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>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
<SidebarRail />
</Sidebar>
<SidebarInset>
<header className="gap-best-friends flex h-16 shrink-0 items-center transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
<div className="gap-best-friends flex items-center px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="#">
Building Your Application
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
<div className="grid auto-rows-min grid-cols-3 gap-4">
<div className="bg-background-muted/50 aspect-video rounded-xl" />
<div className="bg-background-muted/50 aspect-video rounded-xl" />
<div className="bg-background-muted/50 aspect-video rounded-xl" />
</div>
<div className="bg-background-muted/50 min-h-min flex-1 rounded-xl" />
</div>
</SidebarInset>
</SidebarProvider>
</div>
)
}