Docs
侧边栏

侧边栏

一个可组合、可主题化和可定制的侧边栏组件。

sidebar-07

一个可折叠为图标的侧边栏。

侧边栏是构建中最复杂的组件之一。它们是任何应用程序的核心,通常包含很多可移动的部分。

我不喜欢构建侧边栏。因此我构建了 30 多个。各种配置。然后把核心组件提取到 sidebar.tsx 中。

我们现在有了一个坚实的基础来构建。可组合。可主题化。可定制。

浏览组件库

安装

运行以下命令以安装 sidebar.tsx

pnpm dlx shadcn@latest add sidebar

将以下颜色添加到您的 CSS 文件中

上述命令应该为您安装颜色。如果没有,请将以下内容复制并粘贴到您的 CSS 文件中。

我们稍后将在 主题部分 中讨论颜色。

app/globals.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 - 侧边栏容器。
  • SidebarHeaderSidebarFooter - 固定在侧边栏顶部和底部。
  • SidebarContent - 可滚动内容。
  • SidebarGroup - SidebarContent 中的部分。
  • SidebarTrigger - Sidebar 的触发器。
侧边栏结构

用法

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

您的第一个侧边栏

让我们从最基本的侧边栏开始。一个可折叠的侧边栏,带有一个菜单。

在您的应用程序根部添加 SidebarProviderSidebarTrigger

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 创建一个新的侧边栏组件。

components/app-sidebar.tsx
import { Sidebar, SidebarContent } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent />
    </Sidebar>
  )
}

现在,我们来为侧边栏添加一个 SidebarMenu

我们将在 SidebarGroup 中使用 SidebarMenu 组件。

components/app-sidebar.tsx
import { Calendar, Home, Inbox, Search, Settings } from "lucide-react"
 
import {
  Sidebar,
  SidebarContent,
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
} from "@/components/ui/sidebar"
 
// 菜单项。
const items = [
  {
    title: "首页",
    url: "#",
    icon: Home,
  },
  {
    title: "收件箱",
    url: "#",
    icon: Inbox,
  },
  {
    title: "日历",
    url: "#",
    icon: Calendar,
  },
  {
    title: "搜索",
    url: "#",
    icon: Search,
  },
  {
    title: "设置",
    url: "#",
    icon: Settings,
  },
]
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent>
        <SidebarGroup>
          <SidebarGroupLabel>应用程序</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-demo

您的第一个侧边栏。

组件

sidebar.tsx 中的组件设计为可组合,即通过将提供的组件组合在一起来构建您的侧边栏。它们还与其他 shadcn/ui 组件如 DropdownMenuCollapsibleDialog 等良好组合。

如果您需要更改 sidebar.tsx 中的代码,鼓励您这样做。代码是您的。使用 sidebar.tsx 作为起点,构建您自己的。

在接下来的部分中,我们将讨论每个组件及其用法。

SidebarProvider

SidebarProvider 组件用于为 Sidebar 组件提供侧边栏上下文。您应该始终将应用程序包装在 SidebarProvider 组件中。

属性

名称类型描述
defaultOpenboolean侧边栏的默认打开状态。
openboolean侧边栏的打开状态(受控)。
onOpenChange(open: boolean) => void设置侧边栏的打开状态(受控)。

宽度

如果您的应用程序中只有单个侧边栏,您可以使用 sidebar.tsx 中的 SIDEBAR_WIDTHSIDEBAR_WIDTH_MOBILE 变量来设置侧边栏的宽度。

components/ui/sidebar.tsx
const SIDEBAR_WIDTH = "16rem"
const SIDEBAR_WIDTH_MOBILE = "18rem"

对于您应用程序中的多个侧边栏,您可以使用 style 属性设置侧边栏的宽度。

要设置侧边栏的宽度,您可以使用 --sidebar-width--sidebar-width-mobile CSS 变量在 style 属性中。

components/ui/sidebar.tsx
<SidebarProvider
  style={{
    "--sidebar-width": "20rem",
    "--sidebar-width-mobile": "20rem",
  }}
>
  <Sidebar />
</SidebarProvider>

这将处理侧边栏的宽度,同时也处理布局间距。

键盘快捷键

SIDEBAR_KEYBOARD_SHORTCUT 变量用于设置用于打开和关闭侧边栏的键盘快捷键。

触发侧边栏时,您可以在 Mac 上使用 cmd+b 键盘快捷键,在 Windows 上使用 ctrl+b

您可以通过更新 SIDEBAR_KEYBOARD_SHORTCUT 变量来更改键盘快捷键。

components/ui/sidebar.tsx
const SIDEBAR_KEYBOARD_SHORTCUT = "b"

持久化状态

SidebarProvider 支持在页面重载和服务器端渲染之间保持侧边栏状态。它使用 cookie 存储当前的侧边栏状态。当侧边栏状态变化时,会设置一个默认的 cookie 名称为 sidebar_state,其值为当前的打开/关闭状态。然后在后续页面加载时读取此 cookie,以恢复侧边栏状态。

要在 Next.js 中保持侧边栏状态,请像这样在 app/layout.tsx 中设置 SidebarProvider

app/layout.tsx
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>
  )
}

您可以通过更新 sidebar.tsx 中的 SIDEBAR_COOKIE_NAME 变量来更改 cookie 的名称。

components/ui/sidebar.tsx
const SIDEBAR_COOKIE_NAME = "sidebar_state"

主要的 Sidebar 组件,用于渲染一个可折叠的侧边栏。

import { Sidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return <Sidebar />
}

属性

属性类型描述
sideleftright侧边栏的位置。
variantsidebarfloatinginset侧边栏的变种。
collapsibleoffcanvasiconnone侧边栏的可折叠状态。

side

使用 side 属性来更改侧边栏的位置。

可用选项为 leftright

import { Sidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return <Sidebar side="left | right" />
}

variant

使用 variant 属性来更改侧边栏的变种。

可用选项为 sidebarfloatinginset

import { Sidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return <Sidebar variant="sidebar | floating | inset" />
}
<SidebarProvider>
  <Sidebar variant="inset" />
  <SidebarInset>
    <main>{children}</main>
  </SidebarInset>
</SidebarProvider>

collapsible

使用 collapsible 属性使侧边栏可折叠。

可用选项为 offcanvasiconnone

import { Sidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return <Sidebar collapsible="offcanvas | icon | none" />
}
属性描述
offcanvas一个从左或右滑出的可折叠侧边栏。
icon一个折叠为图标的侧边栏。
none不可折叠的侧边栏。

useSidebar

useSidebar 钩子用于控制侧边栏。

import { useSidebar } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  const {
    state,
    open,
    setOpen,
    openMobile,
    setOpenMobile,
    isMobile,
    toggleSidebar,
  } = useSidebar()
}
属性类型描述
stateexpandedcollapsed侧边栏的当前状态。
openboolean侧边栏是否打开。
setOpen(open: boolean) => void设置侧边栏的打开状态。
openMobileboolean侧边栏在移动端是否打开。
setOpenMobile(open: boolean) => void设置侧边栏在移动端的打开状态。
isMobileboolean侧边栏是否在移动端。
toggleSidebar() => void切换侧边栏。桌面和移动端都适用。

SidebarHeader

使用 SidebarHeader 组件为侧边栏添加一个固定的头部。

以下示例向 SidebarHeader 添加了一个 <DropdownMenu>

sidebar-header

带有下拉菜单的侧边栏头部。

components/app-sidebar.tsx
<Sidebar>
  <SidebarHeader>
    <SidebarMenu>
      <SidebarMenuItem>
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <SidebarMenuButton>
              选择工作区
              <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

使用 SidebarFooter 组件为侧边栏添加一个固定的底部。

以下示例向 SidebarFooter 添加了一个 <DropdownMenu>

sidebar-footer

带有下拉菜单的侧边栏底部。

components/app-sidebar.tsx
export function AppSidebar() {
  return (
    <SidebarProvider>
      <Sidebar>
        <SidebarHeader />
        <SidebarContent />
        <SidebarFooter>
          <SidebarMenu>
            <SidebarMenuItem>
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <SidebarMenuButton>
                    <User2 /> 用户名
                    <ChevronUp className="ml-auto" />
                  </SidebarMenuButton>
                </DropdownMenuTrigger>
                <DropdownMenuContent
                  side="top"
                  className="w-[--radix-popper-anchor-width]"
                >
                  <DropdownMenuItem>
                    <span>帐户</span>
                  </DropdownMenuItem>
                  <DropdownMenuItem>
                    <span>账单</span>
                  </DropdownMenuItem>
                  <DropdownMenuItem>
                    <span>退出</span>
                  </DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu>
            </SidebarMenuItem>
          </SidebarMenu>
        </SidebarFooter>
      </Sidebar>
    </SidebarProvider>
  )
}

SidebarContent

SidebarContent 组件用于包装侧边栏的内容。这里是您添加 SidebarGroup 组件的地方。它是可滚动的。

import { Sidebar, SidebarContent } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent>
        <SidebarGroup />
        <SidebarGroup />
      </SidebarContent>
    </Sidebar>
  )
}

SidebarGroup

使用 SidebarGroup 组件在侧边栏中创建一个部分。

SidebarGroup 具有 SidebarGroupLabelSidebarGroupContent 和可选的 SidebarGroupAction

sidebar-group

一个侧边栏组。

import { Sidebar, SidebarContent, SidebarGroup } from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent>
        <SidebarGroup>
          <SidebarGroupLabel>应用程序</SidebarGroupLabel>
          <SidebarGroupAction>
            <Plus /> <span className="sr-only">添加项目</span>
          </SidebarGroupAction>
          <SidebarGroupContent></SidebarGroupContent>
        </SidebarGroup>
      </SidebarContent>
    </Sidebar>
  )
}

可折叠 SidebarGroup

要使 SidebarGroup 可折叠,请将其包装在 Collapsible 中。

sidebar-group-collapsible

一个可折叠的侧边栏组。

export function AppSidebar() {
  return (
    <Collapsible defaultOpen className="group/collapsible">
      <SidebarGroup>
        <SidebarGroupLabel asChild>
          <CollapsibleTrigger>
            帮助
            <ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
          </CollapsibleTrigger>
        </SidebarGroupLabel>
        <CollapsibleContent>
          <SidebarGroupContent />
        </CollapsibleContent>
      </SidebarGroup>
    </Collapsible>
  )
}

SidebarGroupAction

使用 SidebarGroupAction 组件向 SidebarGroup 添加一个操作按钮。

export function AppSidebar() {
  return (
    <SidebarGroup>
      <SidebarGroupLabel asChild>项目</SidebarGroupLabel>
      <SidebarGroupAction title="添加项目">
        <Plus /> <span className="sr-only">添加项目</span>
      </SidebarGroupAction>
      <SidebarGroupContent />
    </SidebarGroup>
  )
}
sidebar-group-action

一个带操作按钮的侧边栏组。

SidebarMenu

SidebarMenu 组件用于在 SidebarGroup 中构建菜单。

SidebarMenu 组件由 SidebarMenuItemSidebarMenuButton<SidebarMenuAction /><SidebarMenuSub /> 组件组成。

侧边栏菜单

以下是一个 SidebarMenu 组件渲染项目列表的示例。

sidebar-menu

一个带项目列表的侧边栏菜单。

<Sidebar>
  <SidebarContent>
    <SidebarGroup>
      <SidebarGroupLabel>项目</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

SidebarMenuButton 组件用于在 SidebarMenuItem 中渲染菜单按钮。

链接或锚点

默认情况下,SidebarMenuButton 渲染为按钮,但您可以使用 asChild 属性渲染其他组件,例如 Linka 标签。

<SidebarMenuButton asChild>
  <a href="#">首页</a>
</SidebarMenuButton>

图标和标签

您可以在按钮内部渲染图标和截断标签。请记得将标签包装在 <span> 中。

<SidebarMenuButton asChild>
  <a href="#">
    <Home />
    <span>首页</span>
  </a>
</SidebarMenuButton>

isActive

使用 isActive 属性将菜单项标记为活动。

<SidebarMenuButton asChild isActive>
  <a href="#">首页</a>
</SidebarMenuButton>

SidebarMenuAction

SidebarMenuAction 组件用于在 SidebarMenuItem 中渲染菜单操作。

这个按钮独立于 SidebarMenuButton 工作,即您可以将 <SidebarMenuButton /> 作为可点击链接,而将 <SidebarMenuAction /> 作为按钮。

<SidebarMenuItem>
  <SidebarMenuButton asChild>
    <a href="#">
      <Home />
      <span>首页</span>
    </a>
  </SidebarMenuButton>
  <SidebarMenuAction>
    <Plus /> <span className="sr-only">添加项目</span>
  </SidebarMenuAction>
</SidebarMenuItem>

以下是 SidebarMenuAction 组件渲染 DropdownMenu 的示例。

sidebar-menu-action

一个带有下拉菜单的侧边栏菜单操作。

<SidebarMenuItem>
  <SidebarMenuButton asChild>
    <a href="#">
      <Home />
      <span>首页</span>
    </a>
  </SidebarMenuButton>
  <DropdownMenu>
    <DropdownMenuTrigger asChild>
      <SidebarMenuAction>
        <MoreHorizontal />
      </SidebarMenuAction>
    </DropdownMenuTrigger>
    <DropdownMenuContent side="right" align="start">
      <DropdownMenuItem>
        <span>编辑项目</span>
      </DropdownMenuItem>
      <DropdownMenuItem>
        <span>删除项目</span>
      </DropdownMenuItem>
    </DropdownMenuContent>
  </DropdownMenu>
</SidebarMenuItem>

SidebarMenuSub

SidebarMenuSub 组件用于在 SidebarMenu 中渲染子菜单。

使用 <SidebarMenuSubItem /><SidebarMenuSubButton /> 渲染子菜单项。

sidebar-menu-sub

一个带有子菜单的侧边栏菜单。

<SidebarMenuItem>
  <SidebarMenuButton />
  <SidebarMenuSub>
    <SidebarMenuSubItem>
      <SidebarMenuSubButton />
    </SidebarMenuSubItem>
    <SidebarMenuSubItem>
      <SidebarMenuSubButton />
    </SidebarMenuSubItem>
  </SidebarMenuSub>
</SidebarMenuItem>

可折叠 SidebarMenu

要使 SidebarMenu 组件可折叠,请将其和 SidebarMenuSub 组件包装在一个 Collapsible 中。

sidebar-menu-collapsible

一个可折叠的侧边栏菜单。

<SidebarMenu>
  <Collapsible defaultOpen className="group/collapsible">
    <SidebarMenuItem>
      <CollapsibleTrigger asChild>
        <SidebarMenuButton />
      </CollapsibleTrigger>
      <CollapsibleContent>
        <SidebarMenuSub>
          <SidebarMenuSubItem />
        </SidebarMenuSub>
      </CollapsibleContent>
    </SidebarMenuItem>
  </Collapsible>
</SidebarMenu>

SidebarMenuBadge

SidebarMenuBadge 组件用于在 SidebarMenuItem 中渲染徽章。

sidebar-menu-badge

一个带徽章的侧边栏菜单。

<SidebarMenuItem>
  <SidebarMenuButton />
  <SidebarMenuBadge>24</SidebarMenuBadge>
</SidebarMenuItem>

SidebarMenuSkeleton

SidebarMenuSkeleton 组件用于为 SidebarMenu 渲染一个骨架。您可以在使用 React 服务器组件、SWR 或 react-query 时使用此骨架来显示加载状态。

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

SidebarSeparator

SidebarSeparator 组件用于在 Sidebar 中渲染分隔符。

<Sidebar>
  <SidebarHeader />
  <SidebarSeparator />
  <SidebarContent>
    <SidebarGroup />
    <SidebarSeparator />
    <SidebarGroup />
  </SidebarContent>
</Sidebar>

SidebarTrigger

使用 SidebarTrigger 组件渲染一个按钮来切换侧边栏。

SidebarTrigger 组件必须在 SidebarProvider 内使用。

<SidebarProvider>
  <Sidebar />
  <main>
    <SidebarTrigger />
  </main>
</SidebarProvider>

自定义触发器

要创建一个自定义触发器,您可以使用 useSidebar 钩子。

import { useSidebar } from "@/components/ui/sidebar"
 
export function CustomTrigger() {
  const { toggleSidebar } = useSidebar()
 
  return <button onClick={toggleSidebar}>切换侧边栏</button>
}

SidebarRail

SidebarRail 组件用于在 Sidebar 中渲染一个轨道。该轨道可用于切换侧边栏。

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

数据提取

React 服务器组件

以下是一个 SidebarMenu 组件使用 React 服务器组件渲染项目列表的示例。

sidebar-rsc

一个使用 React 服务器组件的侧边栏菜单。

显示加载状态的骨架。
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>
  )
}
与 React Suspense 的用法。
function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent>
        <SidebarGroup>
          <SidebarGroupLabel>项目</SidebarGroupLabel>
          <SidebarGroupContent>
            <React.Suspense fallback={<NavProjectsSkeleton />}>
              <NavProjects />
            </React.Suspense>
          </SidebarGroupContent>
        </SidebarGroup>
      </SidebarContent>
    </Sidebar>
  )
}

SWR 和 React Query

您可以使用与 SWRreact-query 相同的方法。

SWR
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>
  )
}
React Query
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>
  )
}

受控侧边栏

使用 openonOpenChange 属性控制侧边栏。

sidebar-controlled

一个受控的侧边栏。

export function AppSidebar() {
  const [open, setOpen] = React.useState(false)
 
  return (
    <SidebarProvider open={open} onOpenChange={setOpen}>
      <Sidebar />
    </SidebarProvider>
  )
}

主题

我们使用以下 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: 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%;
  }
}

我们故意为侧边栏和应用程序的其他部分使用不同的变量,以便轻松实现与应用程序其余部分样式不同的侧边栏。想象一下,侧边栏的颜色比主应用程序深。

样式

这里有一些关于基于不同状态样式化侧边栏的提示。

  • 根据侧边栏的可折叠状态样式化元素。 以下代码将在侧边栏处于 icon 模式时隐藏 SidebarGroup
<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>

您可以在这条 Twitter 线程 中找到有关如何使用状态进行样式化的更多提示。

更新日志

  • #5593 - 改进 <SidebarProvider> 中的 setOpen 回调逻辑。

<SidebarProvider> 中的 setOpen 回调更新如下:

const setOpen = React.useCallback(
  (value: boolean | ((value: boolean) => boolean)) => {
    const openState = typeof value === "function" ? value(open) : value
    if (setOpenProp) {
      setOpenProp(openState)
    } else {
      _setOpen(openState)
    }
 
    // 这将设置 cookie 以保持侧边栏状态。
    document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
  },
  [setOpenProp, open]
)

2024-10-21 修复 text-sidebar-foreground

  • #5491 - 将 text-sidebar-foreground<SidebarProvider> 移动到 <Sidebar> 组件。

2024-10-20 修复 useSidebar 钩子中的拼写错误。

修复 useSidebar 钩子中的拼写错误。

sidebar.tsx
-  throw new Error("useSidebar must be used within a Sidebar.")
+  throw new Error("useSidebar must be used within a SidebarProvider.")