侧边栏 Sidebar
一个可组合、可主题化且可自定义的侧边栏组件

A sidebar that collapses to icons.
侧边栏是最复杂的组件之一,它们是任何应用程序的核心部分,通常包含许多动态元素。
我并不喜欢构建侧边栏,所以我构建了 30 多个不同配置的侧边栏,然后,我将核心组件提取到了 sidebar.tsx 文件中。
现在,我们有了一个坚实的基础,可以在其上进行构建——可组合、可主题化、可自定义。
Installation
运行以下命令进行安装sidebar.tsx
pnpm dlx shadcn@latest add sidebar
将以下颜色添加到您的 CSS 文件中
上面的命令应该会为您安装所需的颜色如果没有,您可以复制并粘贴以下内容到您的 CSS 文件中。
稍后我们将在主题部分中详细讲解颜色的使用。
@layer base {
  :root {
    --sidebar-background: 0 0% 98%;
    --sidebar-foreground: 240 5.3% 26.1%;
    --sidebar-primary: 240 5.9% 10%;
    --sidebar-primary-foreground: 0 0% 98%;
    --sidebar-accent: 240 4.8% 95.9%;
    --sidebar-accent-foreground: 240 5.9% 10%;
    --sidebar-border: 220 13% 91%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }
 
  .dark {
    --sidebar-background: 240 5.9% 10%;
    --sidebar-foreground: 240 4.8% 95.9%;
    --sidebar-primary: 224.3 76.3% 48%;
    --sidebar-primary-foreground: 0 0% 100%;
    --sidebar-accent: 240 3.7% 15.9%;
    --sidebar-accent-foreground: 240 4.8% 95.9%;
    --sidebar-border: 240 3.7% 15.9%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }
}结构
一个 Sidebar 组件由以下部分组成:
- SidebarProvider- 处理可折叠状态
- Sidebar- 侧边栏容器
- SidebarHeader和- SidebarFooter- 固定(Sticky)在侧边栏的顶部和底部
- SidebarContent- 可滚动内容
- SidebarGroup-- SidebarContent中的一个部分
- SidebarTrigger- 触发- Sidebar的按钮
 
使用示例
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>
  )
}import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarHeader,
} from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarHeader />
      <SidebarContent>
        <SidebarGroup />
        <SidebarGroup />
      </SidebarContent>
      <SidebarFooter />
    </Sidebar>
  )
}第一个 Sidebar
让我们从最基础的侧边栏开始,一个可折叠的侧边栏,里面有菜单。
在应用的根目录添加 SidebarProvider 和 SidebarTrigger
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 } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent />
    </Sidebar>
  )
}现在,添加一个 SidebarMenu 到侧边栏
我们将在 SidebarGroup 中使用 SidebarMenu 组件
import { Calendar, Home, Inbox, Search, Settings } from "lucide-react"
 
import {
  Sidebar,
  SidebarContent,
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
} from "@/components/ui/sidebar"
 
// Menu items.
const items = [
  {
    title: "Home",
    url: "#",
    icon: Home,
  },
  {
    title: "Inbox",
    url: "#",
    icon: Inbox,
  },
  {
    title: "Calendar",
    url: "#",
    icon: Calendar,
  },
  {
    title: "Search",
    url: "#",
    icon: Search,
  },
  {
    title: "Settings",
    url: "#",
    icon: Settings,
  },
]
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent>
        <SidebarGroup>
          <SidebarGroupLabel>Application</SidebarGroupLabel>
          <SidebarGroupContent>
            <SidebarMenu>
              {items.map((item) => (
                <SidebarMenuItem key={item.title}>
                  <SidebarMenuButton asChild>
                    <a href={item.url}>
                      <item.icon />
                      <span>{item.title}</span>
                    </a>
                  </SidebarMenuButton>
                </SidebarMenuItem>
              ))}
            </SidebarMenu>
          </SidebarGroupContent>
        </SidebarGroup>
      </SidebarContent>
    </Sidebar>
  )
}你已经创建了你的第一个侧边栏

你的第一个侧边栏
组件
sidebar.tsx 中的组件是可组合的,即你可以通过将提供的组件组合在一起构建侧边栏,它们还可以很好地与其他 shadcn/ui 组件(如 DropdownMenu, Collapsible 或 Dialog 等)一起使用
如果你需要修改 sidebar.tsx 中的代码,可以随意更改。代码是你的,可以以 sidebar.tsx 作为起点并根据需求进行自定义。
在接下来的部分中,我们将详细介绍每个组件及其使用方法
SidebarProvider
SidebarProvider 组件用于提供侧边栏的上下文。你应该始终在你的应用程序中将其包裹在 SidebarProvider 组件中。
Props
| Name | Type | Description | 
|---|---|---|
| defaultOpen | boolean | 侧边栏的默认打开状态 | 
| open | boolean | 侧边栏的打开状态(受控) | 
| onOpenChange | (open: boolean) => void | 设置侧边栏的打开状态(受控) | 
Width
如果你的应用中只有一个侧边栏,你可以在 sidebar.tsx 文件中使用 SIDEBAR_WIDTH 和 SIDEBAR_WIDTH_MOBILE 变量来设置侧边栏的宽度。
const SIDEBAR_WIDTH = "16rem"
const SIDEBAR_WIDTH_MOBILE = "18rem"如果你的应用中有多个侧边栏,你可以使用 style 属性来设置侧边栏的宽度。
你可以在 style 属性中使用 --sidebar-width 和 --sidebar-width-mobile CSS 变量来设置侧边栏的宽度。
<SidebarProvider
  style={{
    "--sidebar-width": "20rem",
    "--sidebar-width-mobile": "20rem",
  }}
>
  <Sidebar />
</SidebarProvider>这将同时处理侧边栏的宽度和布局的间距。
键盘快捷键
SIDEBAR_KEYBOARD_SHORTCUT 变量用于设置打开和关闭侧边栏的键盘快捷键。
在 Mac 上,使用 cmd+b 快捷键,Windows 上使用 ctrl+b 快捷键来触发侧边栏。
你可以通过更新 SIDEBAR_KEYBOARD_SHORTCUT 变量来修改快捷键
const SIDEBAR_KEYBOARD_SHORTCUT = "b"持久化状态
SidebarProvider 支持跨页面重载和服务器端渲染时持久化侧边栏状态。它使用 cookies 来存储当前的侧边栏状态。当侧边栏状态发生变化时,会设置一个默认的 cookie,名为 sidebar:state,它保存当前的开/关状态。这个 cookie 会在后续页面加载时读取,并恢复侧边栏的状态。
要在 Next.js 中持久化侧边栏状态,可以在 app/layout.tsx 中像这样设置 SidebarProvider:
import { cookies } from "next/headers"
 
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { AppSidebar } from "@/components/app-sidebar"
 
export async function Layout({ children }: { children: React.ReactNode }) {
  const cookieStore = await cookies()
  const defaultOpen = cookieStore.get("sidebar_state")?.value === "true"
 
  return (
    <SidebarProvider defaultOpen={defaultOpen}>
      <AppSidebar />
      <main>
        <SidebarTrigger />
        {children}
      </main>
    </SidebarProvider>
  )
}You can change the name of the cookie by updating the SIDEBAR_COOKIE_NAME variable in sidebar.tsx.
const SIDEBAR_COOKIE_NAME = "sidebar_state"Sidebar
The main Sidebar component used to render a collapsible sidebar.
import { Sidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return <Sidebar />
}Props
| Property | Type | Description | 
|---|---|---|
| side | leftorright | The side of the sidebar. | 
| variant | sidebar,floating, orinset | The variant of the sidebar. | 
| collapsible | offcanvas,icon, ornone | Collapsible state of the sidebar. | 
side
Use the side prop to change the side of the sidebar.
Available options are left and right.
import { Sidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return <Sidebar side="left | right" />
}variant
Use the variant prop to change the variant of the sidebar.
Available options are sidebar, floating and inset.
import { Sidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return <Sidebar variant="sidebar | floating | inset" />
}Note: If you use the inset variant, remember to wrap your main content
in a SidebarInset component.
<SidebarProvider>
  <Sidebar variant="inset" />
  <SidebarInset>
    <main>{children}</main>
  </SidebarInset>
</SidebarProvider>collapsible
Use the collapsible prop to make the sidebar collapsible.
Available options are offcanvas, icon and none.
import { Sidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return <Sidebar collapsible="offcanvas | icon | none" />
}| Prop | 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. | 
useSidebar
The useSidebar hook is used to control the sidebar.
import { useSidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  const {
    state,
    open,
    setOpen,
    openMobile,
    setOpenMobile,
    isMobile,
    toggleSidebar,
  } = useSidebar()
}| Property | Type | Description | 
|---|---|---|
| state | expandedorcollapsed | The current state of the sidebar. | 
| open | boolean | Whether the sidebar is open. | 
| setOpen | (open: boolean) => void | Sets the open state of the sidebar. | 
| 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 sidebar is on mobile. | 
| toggleSidebar | () => void | Toggles the sidebar. Desktop and mobile. | 
SidebarHeader
Use the SidebarHeader component to add a sticky header to the sidebar.
The following example adds a <DropdownMenu> to the SidebarHeader.

A sidebar header with a dropdown menu.
<Sidebar>
  <SidebarHeader>
    <SidebarMenu>
      <SidebarMenuItem>
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <SidebarMenuButton>
              Select Workspace
              <ChevronDown className="ml-auto" />
            </SidebarMenuButton>
          </DropdownMenuTrigger>
          <DropdownMenuContent className="w-[--radix-popper-anchor-width]">
            <DropdownMenuItem>
              <span>Acme Inc</span>
            </DropdownMenuItem>
            <DropdownMenuItem>
              <span>Acme Corp.</span>
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </SidebarMenuItem>
    </SidebarMenu>
  </SidebarHeader>
</Sidebar>SidebarFooter
Use the SidebarFooter component to add a sticky footer to the sidebar.
The following example adds a <DropdownMenu> to the SidebarFooter.

A sidebar footer with a dropdown menu.
export function AppSidebar() {
  return (
    <SidebarProvider>
      <Sidebar>
        <SidebarHeader />
        <SidebarContent />
        <SidebarFooter>
          <SidebarMenu>
            <SidebarMenuItem>
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <SidebarMenuButton>
                    <User2 /> Username
                    <ChevronUp className="ml-auto" />
                  </SidebarMenuButton>
                </DropdownMenuTrigger>
                <DropdownMenuContent
                  side="top"
                  className="w-[--radix-popper-anchor-width]"
                >
                  <DropdownMenuItem>
                    <span>Account</span>
                  </DropdownMenuItem>
                  <DropdownMenuItem>
                    <span>Billing</span>
                  </DropdownMenuItem>
                  <DropdownMenuItem>
                    <span>Sign out</span>
                  </DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu>
            </SidebarMenuItem>
          </SidebarMenu>
        </SidebarFooter>
      </Sidebar>
    </SidebarProvider>
  )
}SidebarContent
The SidebarContent component is used to wrap the content of the sidebar. This is where you add your SidebarGroup components. It is scrollable.
import { Sidebar, SidebarContent } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent>
        <SidebarGroup />
        <SidebarGroup />
      </SidebarContent>
    </Sidebar>
  )
}SidebarGroup
Use the SidebarGroup component to create a section within the sidebar.
A SidebarGroup has a SidebarGroupLabel, a SidebarGroupContent and an optional SidebarGroupAction.

A sidebar group.
import { Sidebar, SidebarContent, SidebarGroup } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent>
        <SidebarGroup>
          <SidebarGroupLabel>Application</SidebarGroupLabel>
          <SidebarGroupAction>
            <Plus /> <span className="sr-only">Add Project</span>
          </SidebarGroupAction>
          <SidebarGroupContent></SidebarGroupContent>
        </SidebarGroup>
      </SidebarContent>
    </Sidebar>
  )
}Collapsible SidebarGroup
To make a SidebarGroup collapsible, wrap it in a Collapsible.

A collapsible sidebar group.
export function AppSidebar() {
  return (
    <Collapsible defaultOpen className="group/collapsible">
      <SidebarGroup>
        <SidebarGroupLabel asChild>
          <CollapsibleTrigger>
            Help
            <ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
          </CollapsibleTrigger>
        </SidebarGroupLabel>
        <CollapsibleContent>
          <SidebarGroupContent />
        </CollapsibleContent>
      </SidebarGroup>
    </Collapsible>
  )
}Note: We wrap the CollapsibleTrigger in a SidebarGroupLabel to render
a button.
SidebarGroupAction
Use the SidebarGroupAction component to add an action button to the SidebarGroup.
export function AppSidebar() {
  return (
    <SidebarGroup>
      <SidebarGroupLabel asChild>Projects</SidebarGroupLabel>
      <SidebarGroupAction title="Add Project">
        <Plus /> <span className="sr-only">Add Project</span>
      </SidebarGroupAction>
      <SidebarGroupContent />
    </SidebarGroup>
  )
}
A sidebar group with an action button.
SidebarMenu
The SidebarMenu component is used for building a menu within a SidebarGroup.
A SidebarMenu component is composed of SidebarMenuItem, SidebarMenuButton, <SidebarMenuAction /> and <SidebarMenuSub /> components.
 
Here's an example of a SidebarMenu component rendering a list of projects.

A sidebar menu with a list of projects.
<Sidebar>
  <SidebarContent>
    <SidebarGroup>
      <SidebarGroupLabel>Projects</SidebarGroupLabel>
      <SidebarGroupContent>
        <SidebarMenu>
          {projects.map((project) => (
            <SidebarMenuItem key={project.name}>
              <SidebarMenuButton asChild>
                <a href={project.url}>
                  <project.icon />
                  <span>{project.name}</span>
                </a>
              </SidebarMenuButton>
            </SidebarMenuItem>
          ))}
        </SidebarMenu>
      </SidebarGroupContent>
    </SidebarGroup>
  </SidebarContent>
</Sidebar>SidebarMenuButton
The SidebarMenuButton component is used to render a menu button within a SidebarMenuItem.
Link or Anchor
By default, the SidebarMenuButton renders a button but you can use the asChild prop to render a different component such as a Link or an a tag.
<SidebarMenuButton asChild>
  <a href="#">Home</a>
</SidebarMenuButton>Icon and Label
You can render an icon and a truncated label inside the button. Remember to wrap the label in a <span>.
<SidebarMenuButton asChild>
  <a href="#">
    <Home />
    <span>Home</span>
  </a>
</SidebarMenuButton>isActive
Use the isActive prop to mark a menu item as active.
<SidebarMenuButton asChild isActive>
  <a href="#">Home</a>
</SidebarMenuButton>SidebarMenuAction
The SidebarMenuAction component is used to render a menu action within a SidebarMenuItem.
This button works independently of the SidebarMenuButton i.e you can have the <SidebarMenuButton /> as a clickable link and the <SidebarMenuAction /> as a button.
<SidebarMenuItem>
  <SidebarMenuButton asChild>
    <a href="#">
      <Home />
      <span>Home</span>
    </a>
  </SidebarMenuButton>
  <SidebarMenuAction>
    <Plus /> <span className="sr-only">Add Project</span>
  </SidebarMenuAction>
</SidebarMenuItem>DropdownMenu
Here's an example of a SidebarMenuAction component rendering a DropdownMenu.

A sidebar menu action with a dropdown menu.
<SidebarMenuItem>
  <SidebarMenuButton asChild>
    <a href="#">
      <Home />
      <span>Home</span>
    </a>
  </SidebarMenuButton>
  <DropdownMenu>
    <DropdownMenuTrigger asChild>
      <SidebarMenuAction>
        <MoreHorizontal />
      </SidebarMenuAction>
    </DropdownMenuTrigger>
    <DropdownMenuContent side="right" align="start">
      <DropdownMenuItem>
        <span>Edit Project</span>
      </DropdownMenuItem>
      <DropdownMenuItem>
        <span>Delete Project</span>
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</SidebarMenuItem>SidebarMenuSub
The SidebarMenuSub component is used to render a submenu within a SidebarMenu.
Use <SidebarMenuSubItem /> and <SidebarMenuSubButton /> to render a submenu item.

A sidebar menu with a submenu.
<SidebarMenuItem>
  <SidebarMenuButton />
  <SidebarMenuSub>
    <SidebarMenuSubItem>
      <SidebarMenuSubButton />
    </SidebarMenuSubItem>
    <SidebarMenuSubItem>
      <SidebarMenuSubButton />
    </SidebarMenuSubItem>
  </SidebarMenuSub>
</SidebarMenuItem>Collapsible SidebarMenu
To make a SidebarMenu component collapsible, wrap it and the SidebarMenuSub components in a Collapsible.

A collapsible sidebar menu.
<SidebarMenu>
  <Collapsible defaultOpen className="group/collapsible">
    <SidebarMenuItem>
      <CollapsibleTrigger asChild>
        <SidebarMenuButton />
      </CollapsibleTrigger>
      <CollapsibleContent>
        <SidebarMenuSub>
          <SidebarMenuSubItem />
        </SidebarMenuSub>
      </CollapsibleContent>
    </SidebarMenuItem>
  </Collapsible>
</SidebarMenu>SidebarMenuBadge
The SidebarMenuBadge component is used to render a badge within a SidebarMenuItem.

A sidebar menu with a badge.
<SidebarMenuItem>
  <SidebarMenuButton />
  <SidebarMenuBadge>24</SidebarMenuBadge>
</SidebarMenuItem>SidebarMenuSkeleton
The SidebarMenuSkeleton component is used to render a skeleton for a SidebarMenu. You can use this to show a loading state when using React Server Components, SWR or react-query.
function NavProjectsSkeleton() {
  return (
    <SidebarMenu>
      {Array.from({ length: 5 }).map((_, index) => (
        <SidebarMenuItem key={index}>
          <SidebarMenuSkeleton />
        </SidebarMenuItem>
      ))}
    </SidebarMenu>
  )
}SidebarSeparator
The SidebarSeparator component is used to render a separator within a Sidebar.
<Sidebar>
  <SidebarHeader />
  <SidebarSeparator />
  <SidebarContent>
    <SidebarGroup />
    <SidebarSeparator />
    <SidebarGroup />
  </SidebarContent>
</Sidebar>SidebarTrigger
Use the SidebarTrigger component to render a button that toggles the sidebar.
The SidebarTrigger component must be used within a SidebarProvider.
<SidebarProvider>
  <Sidebar />
  <main>
    <SidebarTrigger />
  </main>
</SidebarProvider>Custom Trigger
To create a custom trigger, you can use the useSidebar hook.
import { useSidebar } from "@/components/ui/sidebar"
 
export function CustomTrigger() {
  const { toggleSidebar } = useSidebar()
 
  return <button onClick={toggleSidebar}>Toggle Sidebar</button>
}SidebarRail
The SidebarRail component is used to render a rail within a Sidebar. This rail can be used to toggle the sidebar.
<Sidebar>
  <SidebarHeader />
  <SidebarContent>
    <SidebarGroup />
  </SidebarContent>
  <SidebarFooter />
  <SidebarRail />
</Sidebar>Data Fetching
React Server Components
Here's an example of a SidebarMenu component rendering a list of projects using React Server Components.

A sidebar menu using React Server Components.
function NavProjectsSkeleton() {
  return (
    <SidebarMenu>
      {Array.from({ length: 5 }).map((_, index) => (
        <SidebarMenuItem key={index}>
          <SidebarMenuSkeleton showIcon />
        </SidebarMenuItem>
      ))}
    </SidebarMenu>
  )
}async function NavProjects() {
  const projects = await fetchProjects()
 
  return (
    <SidebarMenu>
      {projects.map((project) => (
        <SidebarMenuItem key={project.name}>
          <SidebarMenuButton asChild>
            <a href={project.url}>
              <project.icon />
              <span>{project.name}</span>
            </a>
          </SidebarMenuButton>
        </SidebarMenuItem>
      ))}
    </SidebarMenu>
  )
}function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent>
        <SidebarGroup>
          <SidebarGroupLabel>Projects</SidebarGroupLabel>
          <SidebarGroupContent>
            <React.Suspense fallback={<NavProjectsSkeleton />}>
              <NavProjects />
            </React.Suspense>
          </SidebarGroupContent>
        </SidebarGroup>
      </SidebarContent>
    </Sidebar>
  )
}SWR and React Query
You can use the same approach with SWR or react-query.
function NavProjects() {
  const { data, isLoading } = useSWR("/api/projects", fetcher)
 
  if (isLoading) {
    return (
      <SidebarMenu>
        {Array.from({ length: 5 }).map((_, index) => (
          <SidebarMenuItem key={index}>
            <SidebarMenuSkeleton showIcon />
          </SidebarMenuItem>
        ))}
      </SidebarMenu>
    )
  }
 
  if (!data) {
    return ...
  }
 
  return (
    <SidebarMenu>
      {data.map((project) => (
        <SidebarMenuItem key={project.name}>
          <SidebarMenuButton asChild>
            <a href={project.url}>
              <project.icon />
              <span>{project.name}</span>
            </a>
          </SidebarMenuButton>
        </SidebarMenuItem>
      ))}
    </SidebarMenu>
  )
}function NavProjects() {
  const { data, isLoading } = useQuery()
 
  if (isLoading) {
    return (
      <SidebarMenu>
        {Array.from({ length: 5 }).map((_, index) => (
          <SidebarMenuItem key={index}>
            <SidebarMenuSkeleton showIcon />
          </SidebarMenuItem>
        ))}
      </SidebarMenu>
    )
  }
 
  if (!data) {
    return ...
  }
 
  return (
    <SidebarMenu>
      {data.map((project) => (
        <SidebarMenuItem key={project.name}>
          <SidebarMenuButton asChild>
            <a href={project.url}>
              <project.icon />
              <span>{project.name}</span>
            </a>
          </SidebarMenuButton>
        </SidebarMenuItem>
      ))}
    </SidebarMenu>
  )
}Controlled Sidebar
Use the open and onOpenChange props to control the sidebar.

A controlled sidebar.
export function AppSidebar() {
  const [open, setOpen] = React.useState(false)
 
  return (
    <SidebarProvider open={open} onOpenChange={setOpen}>
      <Sidebar />
    </SidebarProvider>
  )
}Theming
We use the following CSS variables to theme the sidebar.
@layer base {
  :root {
    --sidebar-background: 0 0% 98%;
    --sidebar-foreground: 240 5.3% 26.1%;
    --sidebar-primary: 240 5.9% 10%;
    --sidebar-primary-foreground: 0 0% 98%;
    --sidebar-accent: 240 4.8% 95.9%;
    --sidebar-accent-foreground: 240 5.9% 10%;
    --sidebar-border: 220 13% 91%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }
 
  .dark {
    --sidebar-background: 240 5.9% 10%;
    --sidebar-foreground: 240 4.8% 95.9%;
    --sidebar-primary: 0 0% 98%;
    --sidebar-primary-foreground: 240 5.9% 10%;
    --sidebar-accent: 240 3.7% 15.9%;
    --sidebar-accent-foreground: 240 4.8% 95.9%;
    --sidebar-border: 240 3.7% 15.9%;
    --sidebar-ring: 217.2 91.2% 59.8%;
  }
}We intentionally use different variables for the sidebar and the rest of the application to make it easy to have a sidebar that is styled differently from the rest of the application. Think a sidebar with a darker shade from the main application.
Styling
Here are some tips for styling the sidebar based on different states.
- Styling an element based on the sidebar collapsible state. The following will hide the SidebarGroupwhen the sidebar is iniconmode.
<Sidebar collapsible="icon">
  <SidebarContent>
    <SidebarGroup className="group-data-[collapsible=icon]:hidden" />
  </SidebarContent>
</Sidebar>- Styling a menu action based on the menu button active state. The following will force the menu action to be visible when the menu button is active.
<SidebarMenuItem>
  <SidebarMenuButton />
  <SidebarMenuAction className="peer-data-[active=true]/menu-button:opacity-100" />
</SidebarMenuItem>You can find more tips on using states for styling in this Twitter thread.
Changelog
2024-10-30 Cookie handling in setOpen
- #5593 - Improved setOpen callback logic in <SidebarProvider>.
Update the setOpen callback in <SidebarProvider> as follows:
const setOpen = React.useCallback(
  (value: boolean | ((value: boolean) => boolean)) => {
    const openState = typeof value === "function" ? value(open) : value
    if (setOpenProp) {
      setOpenProp(openState)
    } else {
      _setOpen(openState)
    }
 
    // This sets the cookie to keep the sidebar state.
    document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
  },
  [setOpenProp, open]
)2024-10-21 Fixed text-sidebar-foreground
- #5491 - Moved text-sidebar-foregroundfrom<SidebarProvider>to<Sidebar>component.
2024-10-20 Typo in useSidebar hook.
Fixed typo in useSidebar hook.
-  throw new Error("useSidebar must be used within a Sidebar.")
+  throw new Error("useSidebar must be used within a SidebarProvider.")
 






 






