Sidebar

A composable, themeable, and customizable sidebar component ported from shadcn/ui's Base UI version, with first-class RTL support.

Sidebars are one of the most complex components to build — they are central to any application and contain a lot of moving parts. This one ships as a single primitive family: SidebarProvider, Sidebar, and composable header/content/footer/group/menu parts.

Last updated January 1, 1980

CreditsModifiedPublished

Copied from shadcn/ui.

  • Baked in RTL support upstream describes as an upgrade path: physical data-[side] positioning selectors for the fixed panel and rail, a dir prop threaded into the desktop container and the mobile Sheet, and rtl:rotate-180 on the SidebarTrigger icon
  • Replaced remaining physical classes with logical properties where behavior allows: end-1/end-1.5 for menu actions/badges and border-s for sub-menus (with mirrored translate)
  • Uses this repo's Sheet, Tooltip, Button, Input, Separator, Skeleton, useIsMobile (@/hooks/use-media-query), and useControllableState (@/hooks/use-controllable-state) instead of upstream equivalents
  • Hides the mobile close button via SheetContent's showCloseButton={false} instead of a [&>button]:hidden override
  • SidebarMenuSkeleton derives its random width from React.useId so server and hydration markup match

Overview

Installation

Usage

app/layout.tsx
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { AppSidebar } from "@/components/app-sidebar"
 
export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <SidebarProvider>
      <AppSidebar />
      <main>
        <SidebarTrigger />
        {children}
      </main>
    </SidebarProvider>
  )
}
components/app-sidebar.tsx
import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarHeader,
} from "@/components/ui/sidebar"
 
export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarHeader />
      <SidebarContent>
        <SidebarGroup />
        <SidebarGroup />
      </SidebarContent>
      <SidebarFooter />
    </Sidebar>
  )
}

Composition

Use the following composition to build a Sidebar layout:

SidebarProvider
├── Sidebar
│   ├── SidebarHeader
│   ├── SidebarContent
│   │   ├── SidebarGroup
│   │   │   ├── SidebarGroupLabel
│   │   │   ├── SidebarGroupAction
│   │   │   ├── SidebarGroupContent
│   │   │   └── SidebarMenu
│   │   │       ├── SidebarMenuItem
│   │   │       │   ├── SidebarMenuButton
│   │   │       │   ├── SidebarMenuAction
│   │   │       │   └── SidebarMenuBadge
│   │   │       └── SidebarMenuItem
│   │   │           ├── SidebarMenuButton
│   │   │           └── SidebarMenuSub
│   │   │               └── SidebarMenuSubItem
│   │   └── SidebarGroup
│   ├── SidebarFooter
│   └── SidebarRail
├── SidebarInset
└── SidebarTrigger

Structure

  • SidebarProvider — Handles collapsible state, the ⌘B/Ctrl+B shortcut, and provides sidebar context.
  • Sidebar — The main collapsible panel; renders as a fixed panel on desktop and a Sheet on mobile.
  • SidebarHeader — Sticky at the top; branding, titles, or workspace switchers.
  • SidebarFooter — Sticky at the bottom; user menus or settings.
  • SidebarContent — Scrollable region between header and footer.
  • SidebarGroup — Groups related navigation with optional label, action, and content areas.
  • SidebarMenu / SidebarMenuItem — Menu structure for links, badges, actions, and nested submenus.
  • SidebarRail — Hover rail on the panel edge that toggles the sidebar.
  • SidebarInset — Wraps main content when using the inset variant.
  • SidebarTrigger — Control that toggles the sidebar open or collapsed.

Examples

RTL

Pass dir="rtl" together with side="right" for Persian layouts. The panel docks to the right, the rail and trigger icon mirror, and collapsed items show tooltips on the correct side via logical side="inline-end".

SidebarProvider

Provides the sidebar context. Always wrap your application (or layout) in it.

Width

For a single sidebar, edit the constants in sidebar.tsx:

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

For multiple sidebars, set CSS variables through the provider:

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

Keyboard Shortcut

Toggle with ⌘B (Mac) or Ctrl+B (Windows/Linux). Change the key by editing SIDEBAR_KEYBOARD_SHORTCUT in sidebar.tsx.

The main collapsible panel.

PropTypeDescription
sideleft | rightThe side of the sidebar.
variantsidebar | floating | insetThe visual variant of the sidebar.
collapsibleoffcanvas | icon | noneCollapse behavior of the sidebar.
dirltr | rtlText direction of the sidebar contents.

Note: If you use the inset variant, wrap your main content in a SidebarInset component.

<SidebarProvider>
  <Sidebar variant="inset" />
  <SidebarInset>
    <main>{children}</main>
  </SidebarInset>
</SidebarProvider>

useSidebar

Controls the sidebar from anywhere inside SidebarProvider.

import { useSidebar } from "@/components/ui/sidebar"
 
export function CustomTrigger() {
  const { toggleSidebar } = useSidebar()
 
  return <button onClick={toggleSidebar}>Toggle Sidebar</button>
}

Styling

Style based on sidebar state with data attributes:

<Sidebar collapsible="icon">
  <SidebarContent>
    {/* Hide an element when collapsed to icons */}
    <SidebarGroup className="group-data-[collapsible=icon]:hidden" />
  </SidebarContent>
</Sidebar>

API Reference

SidebarProvider

Sidebar

useSidebar

SidebarMenuButton