"use client";
import {
AudioWaveform,
BadgeCheck,
Bell,
BookOpen,
Bot,
ChevronRight,
ChevronsUpDown,
Command,
CreditCard,
Folder,
Forward,
Frame,
GalleryVerticalEnd,
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: "m@example.com",
avatar: "https://github.com/Maqed.png",
},
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: MapIcon },
],
};
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 https://herocn.dev/r/sidebar.jsonimport { 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
└── SidebarTriggerinset variant.The SidebarProvider component provides the sidebar context to the Sidebar component. You should always wrap your application in a SidebarProvider component.
If you have a single sidebar in your application, you can change the SIDEBAR_WIDTH and SIDEBAR_WIDTH_MOBILE constants in sidebar.tsx:
const SIDEBAR_WIDTH = "16rem"
const SIDEBAR_WIDTH_MOBILE = "18rem"For multiple sidebars, use the style prop with CSS variables:
<SidebarProvider
style={
{
"--sidebar-width": "20rem",
"--sidebar-width-mobile": "20rem",
} as React.CSSProperties
}
>
<Sidebar />
</SidebarProvider>The sidebar can be toggled using cmd+b on Mac and ctrl+b on Windows.
const SIDEBAR_KEYBOARD_SHORTCUT = "b"The main Sidebar component renders a collapsible sidebar.
<Sidebar side="left" variant="sidebar" collapsible="offcanvas">
...
</Sidebar>Use the variant prop to control the sidebar appearance:
| Variant | Description |
|---|---|
sidebar | Fixed sidebar that pushes content. |
floating | A floating sidebar with rounded corners and shadow. |
inset | An inset sidebar that wraps content with a SidebarInset component. |
Use the collapsible prop to control how the sidebar collapses:
| Collapsible | Description |
|---|---|
offcanvas | A collapsible sidebar that slides in from the left or right. |
icon | A sidebar that collapses to icons. |
none | A non-collapsible sidebar. |
When using the inset variant, wrap your main content in a SidebarInset:
<SidebarProvider>
<Sidebar variant="inset" />
<SidebarInset>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>The useSidebar hook provides access to the sidebar state:
import { useSidebar } from "@/components/ui/sidebar"
export function CustomTrigger() {
const {
state,
open,
setOpen,
openMobile,
setOpenMobile,
isMobile,
toggleSidebar,
} = useSidebar()
}Use the SidebarHeader component to add a sticky header to the sidebar.
<Sidebar>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger
render={
<SidebarMenuButton>
Select Workspace
</SidebarMenuButton>
}
>
<ChevronDown className="ml-auto" />
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuItem>
<span>Acme Inc</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
</Sidebar>Use the SidebarFooter component to add a sticky footer to the sidebar.
<Sidebar>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton render={<a href="#" />}>
<User2 /> Username
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>The SidebarContent component wraps the scrollable content area of the sidebar.
<Sidebar>
<SidebarContent>
<SidebarGroup />
<SidebarGroup />
</SidebarContent>
</Sidebar>Use the SidebarGroup component to create a section within the sidebar.
<SidebarGroup>
<SidebarGroupLabel>Application</SidebarGroupLabel>
<SidebarGroupAction>
<Plus /> <span className="sr-only">Add Project</span>
</SidebarGroupAction>
<SidebarGroupContent></SidebarGroupContent>
</SidebarGroup>To make a SidebarGroup collapsible, wrap it in a Collapsible:
<Collapsible defaultOpen className="group/collapsible">
<SidebarGroup>
<SidebarGroupLabel render={<CollapsibleTrigger />}>
Help
<ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent />
</CollapsibleContent>
</SidebarGroup>
</Collapsible>The SidebarMenu component builds a menu within a SidebarGroup.
<SidebarMenu>
<SidebarMenuButton render={<a href={item.url} />}>
<item.icon />
<span>{item.name}</span>
</SidebarMenuButton>
</SidebarMenu>The SidebarMenuButton renders a menu button within a SidebarMenuItem. Use the render prop to change the rendered element:
<SidebarMenuButton render={<a href="#" />} isActive>
<Home />
Home
</SidebarMenuButton>When the sidebar is collapsed to icons, use the tooltip prop to show a tooltip on hover:
<SidebarMenuButton tooltip="Home">
<Home />
<span>Home</span>
</SidebarMenuButton>The SidebarMenuAction component renders a contextual action within a SidebarMenuItem.
<SidebarMenuItem>
<SidebarMenuButton render={<a href="#" />}>
<Home />
<span>Home</span>
</SidebarMenuButton>
<SidebarMenuAction showOnHover>
<Plus /> <span className="sr-only">Add</span>
</SidebarMenuAction>
</SidebarMenuItem>Use the showOnHover prop to show the action only on hover or focus.
The SidebarMenuSub component renders a submenu within a SidebarMenu.
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton render={<a href="#" />}>
<span>Sub item</span>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</SidebarMenuItem>The SidebarMenuBadge component renders a badge within a SidebarMenuItem.
<SidebarMenuItem>
<SidebarMenuButton render={<a href="#" />}>
<Inbox />
<span>Inbox</span>
</SidebarMenuButton>
<SidebarMenuBadge>24</SidebarMenuBadge>
</SidebarMenuItem>The SidebarMenuSkeleton component renders loading placeholders for menu items.
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>Use the SidebarTrigger component to render a button that toggles the sidebar.
<SidebarTrigger />You can also use useSidebar to create a custom trigger:
import { useSidebar } from "@/components/ui/sidebar"
export function CustomTrigger() {
const { toggleSidebar } = useSidebar()
return <button onClick={toggleSidebar}>Toggle Sidebar</button>
}The SidebarRail component renders a thin clickable area along the edge of the sidebar for toggling.
<Sidebar>
<SidebarHeader />
<SidebarContent>
<SidebarGroup />
</SidebarContent>
<SidebarFooter />
<SidebarRail />
</Sidebar>Use the open and onOpenChange props to control the sidebar from outside:
export function AppSidebar() {
const [open, setOpen] = React.useState(false)
return (
<SidebarProvider open={open} onOpenChange={setOpen}>
<Sidebar />
</SidebarProvider>
)
}Use Tailwind's group data attributes to style based on sidebar state:
<Sidebar collapsible="icon">
<SidebarContent>
<SidebarGroup className="group-data-[collapsible=icon]:hidden" />
</SidebarContent>
</Sidebar><SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuAction className="peer-data-[active=true]/menu-button:opacity-100" />
</SidebarMenuItem>The following CSS variables control the sidebar appearance:
:root {
--sidebar-background: var(--surface);
--sidebar-foreground: oklch(0.141 0.005 285.823);
--sidebar-primary: oklch(0.6204 0.195 253.83);
--sidebar-primary-foreground: oklch(0.97 0.014 254.604);
--sidebar-accent: oklch(0.93 0.001 286.375);
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
--sidebar-border: oklch(0.92 0.004 286.32);
--sidebar-ring: var(--foreground);
}
.dark {
--sidebar-background: var(--surface);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.6204 0.195 253.83);
--sidebar-primary-foreground: oklch(0.97 0.014 254.604);
--sidebar-accent: oklch(0.274 0.006 286.033);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: var(--foreground);
}"use client";
import {
AudioWaveform,
BadgeCheck,
Bell,
BookOpen,
Bot,
ChevronRight,
ChevronsUpDown,
Command,
CreditCard,
Folder,
Forward,
Frame,
GalleryVerticalEnd,
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: "Acme Inc", logo: GalleryVerticalEnd, 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: "m@example.com",
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 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 |
| open | boolean | — |
| onOpenChange | (open: boolean) => void | — |
| Prop | Type | Default |
|---|---|---|
| side | "left" | "right" | "left" |
| variant | "sidebar" | "floating" | "inset" | "sidebar" |
| collapsible | "offcanvas" | "icon" | "none" | "offcanvas" |
| dir | "ltr" | "rtl" | — |
| Prop | Type | Default |
|---|---|---|
| state | "expanded" | "collapsed" | — |
| open | boolean | — |
| setOpen | (open: boolean) => void | — |
| openMobile | boolean | — |
| setOpenMobile | (open: boolean) => void | — |
| isMobile | boolean | — |
| toggleSidebar | () => void | — |
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "outline" | "default" |
| size | "default" | "sm" | "lg" | "default" |
| isActive | boolean | false |
| tooltip | string | TooltipContentProps | — |
| Prop | Type | Default |
|---|---|---|
| showOnHover | boolean | false |
| Prop | Type | Default |
|---|---|---|
| size | "sm" | "md" | "md" |
| isActive | boolean | false |
| Prop | Type | Default |
|---|---|---|
| showIcon | boolean | false |
On This Page
InstallationUsageCompositionStructureSidebarProviderWidthKeyboard ShortcutSidebaruseSidebarSidebarHeaderSidebarFooterSidebarContentSidebarGroupSidebarMenuSidebarMenuButtonSidebarMenuActionSidebarMenuSubSidebarMenuBadgeSidebarMenuSkeletonSidebarTriggerSidebarRailControlled SidebarStylingThemingRTLAPI ReferenceSidebarProviderSidebaruseSidebarSidebarMenuButtonSidebarMenuActionSidebarMenuSubButtonSidebarMenuSkeleton