Docs
Sidebar

Sidebar

The Sidebar component offers a highly flexible, themeable, and customizable solution for creating application sidebars with various configurations and behaviors.

sidebar-07

A sidebar that collapses to icons.

Developing sidebars can often be a challenging task due to their central role in application navigation and the numerous interactive elements they typically contain. Recognizing this complexity, a comprehensive set of over 30 diverse sidebar configurations were developed to explore various layouts and functionalities. From this extensive exploration, the fundamental building blocks were distilled and organized into the sidebar.tsx file.

This process has resulted in a robust and adaptable foundation for constructing sidebars that are inherently composable, easily themeable to match your application's design, and highly customizable to meet specific requirements.

Explore a wide array of pre-built sidebar examples in the Blocks Library.

Setting Up the Sidebar Component

Execute the following command in your terminal to install the sidebar.tsx component file:

npx shadcn@latest add sidebar

Incorporate the following color variables into your main CSS file (e.g., app/globals.css):

The CLI command executed in the previous step is designed to automatically add these color variables to your CSS. However, if they are not present, please manually copy and paste the following CSS block into your project's stylesheet.

A more detailed explanation of these color variables and how they are used for styling the sidebar can be found in the dedicated Theming section.

@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%;
  }
}

Understanding the Component Structure

The Sidebar component is designed with a modular structure, comprising several distinct parts that can be composed together to build various sidebar layouts and functionalities:

  • SidebarProvider: This component is responsible for managing the collapsible state and providing the necessary context to its descendants.

  • Sidebar: The primary container component that wraps the entire sidebar structure.

  • SidebarHeader and SidebarFooter: Components designed to remain fixed at the top and bottom of the sidebar respectively, often used for branding, user information, or action buttons.

  • SidebarContent: This component serves as a wrapper for the main scrollable area of the sidebar, where groups of navigation items or other content are placed.

  • SidebarGroup: Used to create logical sections or groupings of related items within the SidebarContent.

  • SidebarTrigger: An interactive element, typically a button, that controls the open/closed state of the sidebar.

Sidebar Structure

Basic Implementation Example


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

Building Your Initial Sidebar Structure

Let's begin by constructing a fundamental sidebar implementation. This example will demonstrate a collapsible sidebar that includes a basic navigation menu.

Integrate the SidebarProvider and SidebarTrigger components at the top level of your application's component tree, typically within your main layout file.

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

Create a new React component file, for instance at components/app-sidebar.tsx, to house your custom sidebar implementation.

import { Sidebar, SidebarContent } from "@/components/ui/sidebar";

export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarContent />
    </Sidebar>
  );
}

Next, let's incorporate a SidebarMenu component within your sidebar structure to manage navigation items.

The SidebarMenu component is typically utilized within a SidebarGroup to organize related menu items.

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

Congratulations! You have successfully assembled your initial sidebar component.

sidebar-demo

Your first sidebar.

Detailed Component Breakdown

The individual components provided within sidebar.tsx are designed with composability in mind, allowing you to construct diverse sidebar layouts by combining these building blocks. These components also seamlessly integrate with other components from the shadcn/ui library, such as DropdownMenu, Collapsible, or Dialog, enabling rich and interactive sidebar experiences.

It is important to note that you have full ownership and are encouraged to modify the code within sidebar.tsx to suit your specific project needs. Consider sidebar.tsx as a foundational starting point from which you can build and customize your ideal sidebar implementation.

In the subsequent sections, we will delve into each component individually, explaining its purpose and demonstrating how to effectively utilize it in your sidebar construction.

The SidebarProvider Component

The SidebarProvider component plays a crucial role by establishing the necessary context for the sidebar, particularly for managing its collapsible state. It is essential to wrap your application's main content or layout with the SidebarProvider to ensure the sidebar components function correctly.

Props

NameTypeDescription
defaultOpenbooleanDefault open state of the sidebar.
openbooleanOpen state of the sidebar (controlled).
onOpenChange(open: boolean) => voidSets open state of the sidebar (controlled).

Controlling the Sidebar Width

For applications featuring a single sidebar, you can conveniently manage its width by adjusting the SIDEBAR_WIDTH and SIDEBAR_WIDTH_MOBILE variables directly within the sidebar.tsx file. These variables control the sidebar's width on desktop and mobile breakpoints, respectively.

const SIDEBAR_WIDTH = "16rem";
const SIDEBAR_WIDTH_MOBILE = "18rem";

In scenarios where your application requires multiple sidebars, each potentially with different width requirements, you can leverage the style prop on the SidebarProvider to define specific widths for each instance.

To define the sidebar's width using the style prop, you can utilize the --sidebar-width and --sidebar-width-mobile CSS variables. These variables allow you to set custom widths that will be applied to the sidebar and influence the layout spacing accordingly.

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

Setting these CSS variables via the style prop not only controls the visual width of the sidebar but also automatically manages the corresponding layout spacing to prevent content overlap.

Configuring Keyboard Shortcuts for Toggling

The SIDEBAR_KEYBOARD_SHORTCUT variable allows you to define a custom keyboard combination that users can press to conveniently open or close the sidebar.

By default, the sidebar can be toggled using the Cmd + B keyboard shortcut on macOS and Ctrl + B on Windows operating systems.

You have the flexibility to change this default keyboard shortcut by simply updating the value assigned to the SIDEBAR_KEYBOARD_SHORTCUT variable in your sidebar.tsx file.

const SIDEBAR_KEYBOARD_SHORTCUT = "b";

Implementing Persisted Sidebar State

The SidebarProvider includes built-in support for preserving the sidebar's open or closed state across page reloads and during server-side rendering. This persistence is achieved by utilizing browser cookies to store the current state. Whenever the sidebar's state changes (from open to closed or vice versa), a cookie (by default named sidebar_state) is updated with the latest state. On subsequent page loads, this cookie is read to automatically restore the sidebar to its previous state, providing a seamless user experience.

To enable sidebar state persistence specifically within a Next.js application, configure your SidebarProvider component within the app/layout.tsx file as demonstrated in the following code snippet:

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

Should you need to use a different name for the cookie that stores the sidebar state, you can easily modify the value assigned to the SIDEBAR_COOKIE_NAME variable within the sidebar.tsx file.

const SIDEBAR_COOKIE_NAME = "sidebar_state";

The Primary Sidebar Component

The Sidebar component serves as the fundamental container for rendering a collapsible sidebar interface within your application layout.

import { Sidebar } from "@/components/ui/sidebar";

export function AppSidebar() {
  return <Sidebar />;
}

Available Properties

PropertyTypeDescription
sideleft or rightThe side of the sidebar.
variantsidebar, floating, or insetThe variant of the sidebar.
collapsibleoffcanvas, icon, or noneCollapsible state of the sidebar.

Controlling the Sidebar's Position

The side prop allows you to determine which edge of the screen the sidebar will be positioned on.

You can set the side prop to either left or right to control the sidebar's horizontal alignment.

import { Sidebar } from "@/components/ui/sidebar";

export function AppSidebar() {
  return <Sidebar side="left | right" />;
}

Exploring Sidebar Variants

The variant prop enables you to select from different visual styles and behaviors for the sidebar component.

The available options for the variant prop are sidebar, floating, and inset, each offering a distinct presentation.

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>

Configuring Collapsible Behavior

The collapsible prop is used to define how the sidebar behaves when it is collapsed or expanded.

You can choose from the following options for the collapsible prop: offcanvas, icon, and none.

import { Sidebar } from "@/components/ui/sidebar";

export function AppSidebar() {
  return <Sidebar collapsible="offcanvas | icon | none" />;
}
PropDescription
offcanvasConfigures the sidebar to slide in and out from the specified side edge.
iconCauses the sidebar to collapse down to display only icons when not expanded.
noneDisables the collapsible functionality, keeping the sidebar permanently open.

The useSidebar Hook

The useSidebar hook provides programmatic access to control the state and behavior of the sidebar component from within your functional components.

import { useSidebar } from "@/components/ui/sidebar";

export function AppSidebar() {
  const {
    state,
    open,
    setOpen,
    openMobile,
    setOpenMobile,
    isMobile,
    toggleSidebar,
  } = useSidebar();
}
PropertyTypeDescription
stateexpanded or collapsedIndicates the current visual state of the sidebar, either fully expanded or collapsed.
openbooleanA boolean value representing whether the sidebar is currently in an open state.
setOpen(open: boolean) => voidA function to programmatically set the open or closed state of the sidebar.
openMobilebooleanA boolean value indicating whether the sidebar is open specifically on mobile viewports.
setOpenMobile(open: boolean) => voidA function to control the open or closed state of the sidebar when viewed on mobile devices.
isMobilebooleanA boolean value that is true when the application is being viewed on a mobile breakpoint.
toggleSidebar() => voidA convenient function to toggle the sidebar's state, applicable to both desktop and mobile views.

The SidebarHeader Component

The SidebarHeader component is designed to contain content that should remain fixed at the top of the sidebar, providing a persistent area for elements like logos, titles, or user information.

The example below illustrates how to integrate a <DropdownMenu> component within the SidebarHeader to create interactive header elements.

sidebar-header

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>

The SidebarFooter Component

The SidebarFooter component is utilized to place content that should remain fixed at the bottom of the sidebar, commonly used for elements such as user profiles, settings links, or application version information.

The subsequent example demonstrates how to embed a <DropdownMenu> component within the SidebarFooter to provide interactive options at the base of the sidebar.

sidebar-footer

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

The SidebarContent Component

The SidebarContent component serves as the primary wrapper for the main content area of the sidebar. This is the designated region where you should place your SidebarGroup components to organize navigation items or other content. The SidebarContent is inherently scrollable, accommodating content that exceeds the sidebar's height.

import { Sidebar, SidebarContent } from "@/components/ui/sidebar";

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

The SidebarGroup Component

The SidebarGroup component is employed to logically segment and organize related content or navigation items within the SidebarContent area.

A typical SidebarGroup structure includes a SidebarGroupLabel for a title or heading, SidebarGroupContent to wrap the group's items, and an optional SidebarGroupAction for adding interactive elements like buttons next to the label.

sidebar-group

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

Creating a Collapsible SidebarGroup

To enable collapsible functionality for a SidebarGroup, you can wrap the SidebarGroup component within a Collapsible component from the shadcn/ui library.

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

The SidebarGroupAction Component

The SidebarGroupAction component is used to render an interactive element, such as a button or icon, typically positioned alongside the SidebarGroupLabel to provide quick actions related to the group.

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

A sidebar group with an action button.

The SidebarMenu Component

The SidebarMenu component is specifically designed for constructing navigation menus or lists of interactive items within a SidebarGroup.

A SidebarMenu is typically composed of several sub-components, including SidebarMenuItem for individual items, SidebarMenuButton for the clickable element, <SidebarMenuAction /> for secondary actions per item, and <SidebarMenuSub /> for nested submenus.

Sidebar Menu

The following example demonstrates how to use the SidebarMenu component to render a list of project items within a sidebar group.

sidebar-menu

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>

The SidebarMenuButton Component

The SidebarMenuButton component is the interactive element within a SidebarMenuItem that users click to trigger an action or navigate to a different location.

Rendering as a Link or Anchor

By default, the SidebarMenuButton is rendered as a standard HTML button element. However, you can utilize the asChild prop to render it as a different component, such as a Next.js Link component or a standard HTML <a> tag, allowing for navigation.

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

Including Icons and Labels

You can easily include both an icon and a text label within the SidebarMenuButton. For proper styling and potential text truncation, ensure that the text label is wrapped within a <span> element.

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

Indicating Active State

The isActive prop is a boolean property that can be used to visually highlight a menu item, indicating that it represents the currently active page or section.

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

The SidebarMenuAction Component

The SidebarMenuAction component provides a designated area within a SidebarMenuItem to include secondary actions or interactive elements related to that specific menu item.

A key feature of the SidebarMenuAction is its independence from the SidebarMenuButton. This allows for flexible designs where the <SidebarMenuButton /> might function as a navigation link, while the <SidebarMenuAction /> provides a separate, clickable button for a related action.

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

Integrating a DropdownMenu

This example demonstrates how to embed a DropdownMenu component within a SidebarMenuAction to present a list of additional options when the action is triggered.

sidebar-menu-action

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>

The SidebarMenuSub Component

The SidebarMenuSub component is designed to create nested menus or submenus that appear when a parent menu item is interacted with, allowing for hierarchical navigation structures.

Within a SidebarMenuSub, you should use the <SidebarMenuSubItem /> component for each individual item in the submenu, and <SidebarMenuSubButton /> for the clickable element within each sub-item.

sidebar-menu-sub

A sidebar menu with a submenu.

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

Creating a Collapsible SidebarMenu

To enable the ability to expand and collapse a SidebarMenu and its associated <SidebarMenuSub /> components, wrap them together within a Collapsible component.

sidebar-menu-collapsible

A collapsible sidebar menu.

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

The SidebarMenuBadge Component

The SidebarMenuBadge component allows you to display a small indicator or badge, often containing a number or short text, alongside a SidebarMenuItem to convey additional information such as unread counts or status.

sidebar-menu-badge

A sidebar menu with a badge.

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

The SidebarMenuSkeleton Component

The SidebarMenuSkeleton component provides a placeholder visual representation of a SidebarMenu while its content is being loaded. This is particularly useful for indicating loading states when fetching data with techniques like React Server Components, SWR, or react-query.

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

The SidebarSeparator Component

The SidebarSeparator component is used to insert a visual divider line within the sidebar structure, helping to visually group or separate different sections or components.

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

The SidebarTrigger Component

The SidebarTrigger component is a dedicated interactive element, typically rendered as a button, whose primary function is to toggle the open or closed state of the sidebar.

It is mandatory that the SidebarTrigger component is placed within the scope of a SidebarProvider to access the necessary context for controlling the sidebar's state.

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

Creating a Custom Trigger Element

If you require a more customized element to trigger the sidebar's toggle action, you can achieve this by utilizing the useSidebar hook to access the toggleSidebar function.

import { useSidebar } from "@/components/ui/sidebar";

export function CustomTrigger() {
  const { toggleSidebar } = useSidebar();

  return <button onClick={toggleSidebar}>Toggle Sidebar</button>;
}

The SidebarRail Component

The SidebarRail component provides a dedicated narrow area, often positioned along the edge of the sidebar, which can be used as an alternative or additional method for toggling the sidebar's visibility.

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

Integrating Data Fetching

Utilizing React Server Components

This section provides an example demonstrating how to populate a SidebarMenu component with a list of projects by fetching data using React Server Components.

sidebar-rsc

An illustration of a sidebar menu dynamically populated with data fetched via 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>
  );
}

Integration with SWR and React Query

The approach demonstrated for React Server Components can be similarly applied when fetching data using client-side data fetching libraries such as 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>
  )
}

Implementing a Controlled Sidebar

To programmatically manage the open or closed state of the sidebar from a parent component, you can utilize the open and onOpenChange props provided by the SidebarProvider.

sidebar-controlled

An illustration of a sidebar whose state is managed externally by a parent component.

export function AppSidebar() {
  const [open, setOpen] = React.useState(false);

  return (
    <SidebarProvider open={open} onOpenChange={setOpen}>
      <Sidebar />
    </SidebarProvider>
  );
}

Customizing the Sidebar Theme

The visual appearance and theming of the sidebar are controlled through the following set of CSS variables.

@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.

Advanced Styling Techniques

Here are some helpful tips and techniques for applying styles to the sidebar based on its various states and the states of its child components.

  • Applying styles based on the sidebar's collapsible state: You can conditionally style elements within the sidebar based on its current collapsible state. The following example demonstrates how to hide a SidebarGroup component when the sidebar is configured for icon mode.

<Sidebar collapsible="icon">
  <SidebarContent>
    <SidebarGroup className="group-data-[collapsible=icon]:hidden" />
  </SidebarContent>
</Sidebar>
  • Styling a menu action based on the menu item's active state: It's possible to apply specific styles to a SidebarMenuAction component based on whether its corresponding SidebarMenuButton is currently in an active state. The following snippet shows how to ensure a menu action remains 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.

Component Changelog

2024-10-30: Updates to Cookie Handling within setOpen Function

  • Referencing Pull Request #5593 - Enhancements have been made to the logic within the setOpen callback function used in the <SidebarProvider> component.

To incorporate these improvements, update the implementation of the setOpen callback function within your <SidebarProvider> as shown below:

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: Resolution of text-sidebar-foreground Styling Issue

  • Referencing Pull Request #5491 - The text-sidebar-foreground styling utility has been relocated from the <SidebarProvider> component to the <Sidebar> component to ensure correct application.

2024-10-20: Correction of Typographical Error in useSidebar Hook

A typographical error within the useSidebar hook implementation has been corrected.

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