Command Palette

Search for a command to run...

Github

Sidebar

A composable, themeable and customizable sidebar component.

"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: "[email protected]",
		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>
	);
}

Installation

pnpm dlx shadcn@latest add https://herocn.dev/r/sidebar.json

Usage

app/layout.tsx
import { 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>
  )
}
components/app-sidebar.tsx
import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarHeader,
} from "@/components/ui/sidebar"

export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarHeader />
      <SidebarContent>
        <SidebarGroup />
        <SidebarGroup />
      </SidebarContent>
      <SidebarFooter />
    </Sidebar>
  )
}

Composition

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
└── SidebarTrigger

Structure

  • SidebarProvider — Handles collapsible state and provides sidebar context to child components.
  • Sidebar — The main collapsible sidebar panel.
  • SidebarHeader — Sticky at the top; use for branding, titles, or workspace switchers.
  • SidebarFooter — Sticky at the bottom; use for user menus, settings, or actions.
  • SidebarContent — Scrollable region between the header and footer.
  • SidebarGroup — Groups related navigation with optional label, action, and content areas.
  • SidebarMenu / SidebarMenuItem — Menu structure for links, badges, actions, and nested submenus.
  • SidebarRail — A thin clickable rail that toggles the sidebar.
  • SidebarInset — Wraps main content when using the inset variant.
  • SidebarTrigger — Control that toggles the sidebar open or collapsed.

SidebarProvider

The SidebarProvider component provides the sidebar context to the Sidebar component. You should always wrap your application in a SidebarProvider component.

Width

If you have a single sidebar in your application, you can change the SIDEBAR_WIDTH and SIDEBAR_WIDTH_MOBILE constants in sidebar.tsx:

components/ui/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>

Keyboard Shortcut

The sidebar can be toggled using cmd+b on Mac and ctrl+b on Windows.

components/ui/sidebar.tsx
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:

VariantDescription
sidebarFixed sidebar that pushes content.
floatingA floating sidebar with rounded corners and shadow.
insetAn inset sidebar that wraps content with a SidebarInset component.

Use the collapsible prop to control how the sidebar collapses:

CollapsibleDescription
offcanvasA collapsible sidebar that slides in from the left or right.
iconA sidebar that collapses to icons.
noneA 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>

useSidebar

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()
}

SidebarHeader

Use the SidebarHeader component to add a sticky header to the sidebar.

components/app-sidebar.tsx
<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>

SidebarFooter

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>

SidebarContent

The SidebarContent component wraps the scrollable content area of the sidebar.

<Sidebar>
  <SidebarContent>
    <SidebarGroup />
    <SidebarGroup />
  </SidebarContent>
</Sidebar>

SidebarGroup

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>

SidebarMenu

The SidebarMenu component builds a menu within a SidebarGroup.

<SidebarMenu>
  <SidebarMenuButton render={<a href={item.url} />}>
    <item.icon />
    <span>{item.name}</span>
  </SidebarMenuButton>
</SidebarMenu>

SidebarMenuButton

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>

SidebarMenuAction

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.

SidebarMenuSub

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>

SidebarMenuBadge

The SidebarMenuBadge component renders a badge within a SidebarMenuItem.

<SidebarMenuItem>
  <SidebarMenuButton render={<a href="#" />}>
    <Inbox />
    <span>Inbox</span>
  </SidebarMenuButton>
  <SidebarMenuBadge>24</SidebarMenuBadge>
</SidebarMenuItem>

SidebarMenuSkeleton

The SidebarMenuSkeleton component renders loading placeholders for menu items.

<SidebarMenu>
  {Array.from({ length: 5 }).map((_, index) => (
    <SidebarMenuItem key={index}>
      <SidebarMenuSkeleton showIcon />
    </SidebarMenuItem>
  ))}
</SidebarMenu>

SidebarTrigger

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>
}

SidebarRail

The SidebarRail component renders a thin clickable area along the edge of the sidebar for toggling.

<Sidebar>
  <SidebarHeader />
  <SidebarContent>
    <SidebarGroup />
  </SidebarContent>
  <SidebarFooter />
  <SidebarRail />
</Sidebar>

Controlled 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>
  )
}

Styling

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>

Theming

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);
}

RTL

"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: "[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 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>
	);
}

API Reference

SidebarProvider

PropTypeDefault
PropTypeDefault

useSidebar

PropTypeDefault

SidebarMenuButton

PropTypeDefault

SidebarMenuAction

PropTypeDefault

SidebarMenuSubButton

PropTypeDefault

SidebarMenuSkeleton

PropTypeDefault