89.1k

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

sidebar-07

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

侧边栏是构建起来最复杂的组件之一。它们是任何应用程序的核心,通常包含许多活动部件。

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

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

浏览区块库.

安装

运行以下命令安装sidebar.tsx

pnpm dlx shadcn@latest add sidebar

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

上面的命令应该会为您安装颜色。如果不行,请将以下内容复制粘贴到您的CSS文件中。

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

app/globals.css
@layer base {
  :root {
    --sidebar: oklch(0.985 0 0);
    --sidebar-foreground: oklch(0.145 0 0);
    --sidebar-primary: oklch(0.205 0 0);
    --sidebar-primary-foreground: oklch(0.985 0 0);
    --sidebar-accent: oklch(0.97 0 0);
    --sidebar-accent-foreground: oklch(0.205 0 0);
    --sidebar-border: oklch(0.922 0 0);
    --sidebar-ring: oklch(0.708 0 0);
  }
 
  .dark {
    --sidebar: oklch(0.205 0 0);
    --sidebar-foreground: oklch(0.985 0 0);
    --sidebar-primary: oklch(0.488 0.243 264.376);
    --sidebar-primary-foreground: oklch(0.985 0 0);
    --sidebar-accent: oklch(0.269 0 0);
    --sidebar-accent-foreground: oklch(0.985 0 0);
    --sidebar-border: oklch(1 0 0 / 10%);
    --sidebar-ring: oklch(0.439 0 0);
  }
}

结构

一个Sidebar组件由以下部分组成

  • SidebarProvider - 处理可折叠状态。
  • Sidebar - 侧边栏容器。
  • SidebarHeaderSidebarFooter - 固定在侧边栏的顶部和底部。
  • SidebarContent - 可滚动内容。
  • SidebarGroup - SidebarContent中的部分。
  • SidebarTrigger - Sidebar的触发器。
Sidebar Structure

用法

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"
 
// 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-demo

您的第一个侧边栏。

组件

sidebar.tsx中的组件被设计为可组合的,即您可以通过组合提供的组件来构建您的侧边栏。它们还可以与DropdownMenuCollapsibleDialog等其他shadcn/ui组件很好地组合。

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

在接下来的章节中,我们将逐一介绍每个组件及其用法。

SidebarProvider

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

Props

名称类型描述
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属性来设置侧边栏的宽度。

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

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来存储侧边栏的当前状态。当侧边栏状态改变时,会设置一个名为sidebar_state的默认cookie,其中包含当前的打开/关闭状态。然后在后续页面加载时读取此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 />
}

Props

属性类型描述
sideleftright侧边栏的位置。
variantsidebar, floating, 或 inset侧边栏的变体。
collapsibleoffcanvas, icon, 或 none侧边栏的可折叠状态。

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" />
}
Prop描述
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>
              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

使用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 /> 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

SidebarContent组件用于包裹侧边栏的内容。您可以在此添加您的SidebarGroup组件。它是可滚动的。

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

SidebarGroup

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

一个SidebarGroup包含一个SidebarGroupLabel、一个SidebarGroupContent和一个可选的SidebarGroupAction

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

可折叠的SidebarGroup

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

sidebar-group-collapsible

一个可折叠的侧边栏组。

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

SidebarGroupAction

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

sidebar-group-action

带有操作按钮的侧边栏组。

export function AppSidebar() {
  return (
    <SidebarGroup>
      <SidebarGroupLabel asChild>Projects</SidebarGroupLabel>
      <SidebarGroupAction title="Add Project">
        <Plus /> <span className="sr-only">Add Project</span>
      </SidebarGroupAction>
      <SidebarGroupContent />
    </SidebarGroup>
  )
}

SidebarMenu

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

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

Sidebar Menu

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

sidebar-menu

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

<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

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

默认情况下,SidebarMenuButton渲染一个按钮,但您可以使用asChild属性来渲染不同的组件,例如Linka标签。

<SidebarMenuButton asChild>
  <a href="#">Home</a>
</SidebarMenuButton>

图标和标签

您可以在按钮内部渲染一个图标和一个截断的标签。请记住将标签包裹在<span>中。

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

isActive

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

<SidebarMenuButton asChild isActive>
  <a href="#">Home</a>
</SidebarMenuButton>

SidebarMenuAction

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

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

<SidebarMenuItem>
  <SidebarMenuButton asChild>
    <a href="#">
      <Home />
      <span>Home</span>
    </a>
  </SidebarMenuButton>
  <SidebarMenuAction>
    <Plus /> <span className="sr-only">Add Project</span>
  </SidebarMenuAction>
</SidebarMenuItem>

这是一个SidebarMenuAction组件渲染DropdownMenu的示例。

sidebar-menu-action

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

<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

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 Server Components、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}>Toggle Sidebar</button>
}

SidebarRail

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

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

数据获取

React 服务器组件

这是一个SidebarMenu组件使用React Server Components渲染项目列表的示例。

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>Projects</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)
    }
 
    // 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 修复了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.")