Capítulo 39 de 108

Sidebar

Core Idea

A composable, themeable, customizable sidebar system. Use as the standard app-shell navigation pattern (collapsible left/right panel with header, scrollable content, footer, and a rail) instead of hand-rolling layout CSS.

Key Concepts

  • SidebarProvider: root context provider handling open/collapsed state and mobile detection; must always wrap the app. Props: defaultOpen, open + onOpenChange (controlled).
  • Sidebar: the main panel. Props: side (left/right), variant (sidebar/floating/inset), collapsible (offcanvas/icon/none).
  • SidebarInset: required wrapper for main content when Sidebar uses variant="inset".
  • useSidebar(): hook exposing state (expanded/collapsed), open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar.
  • Width via CSS vars: single sidebar → edit SIDEBAR_WIDTH/SIDEBAR_WIDTH_MOBILE constants in sidebar.tsx; multiple sidebars → pass --sidebar-width/--sidebar-width-mobile in the style prop of SidebarProvider.
  • Keyboard shortcut: cmd+b (Mac) / ctrl+b (Windows) toggles the sidebar by default, defined by SIDEBAR_KEYBOARD_SHORTCUT = "b" in sidebar.tsx.
  • SidebarMenuButton: renders a button by default; use render prop to swap for <a>/Link; isActive prop marks it as the active item.
  • Collapsible groups/menus: nest SidebarGroup/SidebarMenuItem inside a Collapsible (from @/components/ui/collapsible), wiring SidebarGroupLabel/SidebarMenuButton with render={<CollapsibleTrigger />}.
  • group-data-[collapsible=icon]:hidden: Tailwind data-attribute pattern to hide elements (e.g. a SidebarGroup) only when the sidebar is collapsed to icon mode.
  • dir prop: Sidebar accepts dir="rtl" for right-to-left layouts; requires the component's internal RTL wiring (see Anti-patterns).

Code Examples

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>
  )
}
  • O que demonstra: setup mínimo obrigatório no layout raiz.
import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarHeader,
} from "@/components/ui/sidebar"

export function AppSidebar() {
  return (
    <Sidebar>
      <SidebarHeader />
      <SidebarContent>
        <SidebarGroup />
        <SidebarGroup />
      </SidebarContent>
      <SidebarFooter />
    </Sidebar>
  )
}
  • O que demonstra: esqueleto estrutural do sidebar da aplicação.
<SidebarProvider>
  <Sidebar variant="inset" />
  <SidebarInset>
    <main>{children}</main>
  </SidebarInset>
</SidebarProvider>
  • O que demonstra: variante inset exige envolver o conteúdo principal em SidebarInset.
<Collapsible defaultOpen className="group/collapsible">
  <SidebarGroup>
    <SidebarGroupLabel render={<CollapsibleTrigger />}>
      Help
      <ChevronDown className="ml-auto transition-transform group-data-open/collapsible:rotate-180" />
    </SidebarGroupLabel>
    <CollapsibleContent>
      <SidebarGroupContent />
    </CollapsibleContent>
  </SidebarGroup>
</Collapsible>
  • O que demonstra: grupo de navegação colapsável usando Collapsible + render no SidebarGroupLabel.
export function AppSidebar() {
  const [open, setOpen] = React.useState(false)
  return (
    <SidebarProvider open={open} onOpenChange={setOpen}>
      <Sidebar />
    </SidebarProvider>
  )
}
  • O que demonstra: sidebar controlado externamente via open/onOpenChange.

Composition

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

Reference Tables

SidebarProvider props

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

Sidebar props

PropertyTypeDescription
sideleft | rightWhich side the sidebar renders on.
variantsidebar | floating | insetVisual variant. inset requires SidebarInset around main content.
collapsibleoffcanvas | icon | noneoffcanvas slides in/out; icon collapses to icon rail; none is non-collapsible.

useSidebar() return

PropertyTypeDescription
stateexpanded | collapsedCurrent sidebar state.
open / setOpenboolean / setterDesktop open state.
openMobile / setOpenMobileboolean / setterMobile open state.
isMobilebooleanWhether running in mobile view.
toggleSidebar() => voidToggles sidebar on both desktop and mobile.

Anti-patterns

  • Usar variant="inset" sem SidebarInset: quebra o layout, SidebarInset é obrigatório para essa variante.
  • Aplicar RTL só passando dir="rtl" numa versão antiga do componente: versões anteriores do Sidebar precisam de patch manual (propagar dir ao SheetContent mobile, adicionar data-side, trocar classes de posicionamento por seletores data-[side=...], e className="rtl:rotate-180" no ícone do SidebarTrigger) antes que dir funcione corretamente.
  • Esconder SidebarGroup manualmente com JS condicional no modo ícone: usar a classe group-data-[collapsible=icon]:hidden, não lógica de renderização condicional.

Key Takeaways

  1. SidebarProvider é obrigatório e único ponto de estado (open/collapsed/mobile) para toda a árvore de sidebar.
  2. collapsible="icon" + classes group-data-[collapsible=icon]:* é o padrão para esconder/ajustar conteúdo quando colapsado, sem lógica JS extra.
  3. Largura é controlada por CSS vars (--sidebar-width), não por prop numérica direta.
  4. Grupos e itens de menu colapsáveis reutilizam o componente Collapsible genérico via render prop, não uma prop collapsible embutida em SidebarMenuItem.
  5. Tema usa variáveis CSS próprias (--sidebar-background, --sidebar-primary, etc.), separadas das variáveis de tema geral do shadcn.
  6. Atalho de teclado padrão é cmd/ctrl+b, configurável editando a constante SIDEBAR_KEYBOARD_SHORTCUT no arquivo fonte.

Connects To

  • Collapsible: primitivo usado internamente para grupos/itens de menu expansíveis dentro do sidebar.
  • DropdownMenu: padrão comum para team switcher e menu de usuário no SidebarHeader/SidebarFooter.
  • Breadcrumb: tipicamente usado junto no header do conteúdo principal ao lado de SidebarTrigger.
  • Sheet: usado internamente pelo Sidebar para a versão mobile (off-canvas).