Sidebar
App shell navigation beside the page: a column of sections and items from the start edge, with a trigger that collapses it to a rail of icons (each named by a Tooltip) or hides it. Below the md breakpoint the same content opens in a Sheet.
Preview
import { IconChartLine, IconHome, IconSettings, IconUser } from "@tabler/icons-react"
import { Sidebar, SidebarContent, SidebarItem, SidebarProvider, SidebarSection, SidebarTrigger } from "@/components/ui/sidebar"
<SidebarProvider>
<SidebarTrigger label="Toggle navigation" />
<Sidebar>
<SidebarContent>
<SidebarSection label="Workspace">
<SidebarItem href="#" leadingIcon={<IconHome size={20} weight="regular" />}>Overview</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconChartLine size={20} weight="regular" />}>Reports</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconSettings size={20} weight="regular" />}>Settings</SidebarItem>
</SidebarSection>
<SidebarSection label="Account">
<SidebarItem href="#" leadingIcon={<IconUser size={20} weight="regular" />}>Profile</SidebarItem>
</SidebarSection>
</SidebarContent>
</Sidebar>
</SidebarProvider>Usage
When to use
- Top-level navigation that stays beside the page across a whole product.
- A long list of destinations grouped into named sections.
When not to use
- Switching between views of the same content. Use Tabs.
- A short list of actions anchored to a control. Use DropdownMenu.
- One optional section on a page. Use Accordion.
Variants
collapsible
Set on SidebarProvider. icon keeps a rail of icons with Tooltips when collapsed; offcanvas hides the sidebar on desktop entirely; none fixes it open.
import { IconChartLine, IconHome, IconSettings, IconUser } from "@tabler/icons-react"
import { Sidebar, SidebarContent, SidebarItem, SidebarProvider, SidebarSection, SidebarTrigger } from "@/components/ui/sidebar"
<SidebarProvider>
<SidebarTrigger label="Toggle navigation" />
<Sidebar>
<SidebarContent>
<SidebarSection label="Workspace">
<SidebarItem href="#" leadingIcon={<IconHome size={20} weight="regular" />}>Overview</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconChartLine size={20} weight="regular" />}>Reports</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconSettings size={20} weight="regular" />}>Settings</SidebarItem>
</SidebarSection>
<SidebarSection label="Account">
<SidebarItem href="#" leadingIcon={<IconUser size={20} weight="regular" />}>Profile</SidebarItem>
</SidebarSection>
</SidebarContent>
</Sidebar>
</SidebarProvider>
<SidebarProvider collapsible="offcanvas">
<SidebarTrigger label="Toggle navigation" />
<Sidebar>
<SidebarContent>
<SidebarSection label="Workspace">
<SidebarItem href="#" leadingIcon={<IconHome size={20} weight="regular" />}>Overview</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconChartLine size={20} weight="regular" />}>Reports</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconSettings size={20} weight="regular" />}>Settings</SidebarItem>
</SidebarSection>
<SidebarSection label="Account">
<SidebarItem href="#" leadingIcon={<IconUser size={20} weight="regular" />}>Profile</SidebarItem>
</SidebarSection>
</SidebarContent>
</Sidebar>
</SidebarProvider>
<SidebarProvider collapsible="none">
<SidebarTrigger label="Toggle navigation" />
<Sidebar>
<SidebarContent>
<SidebarSection label="Workspace">
<SidebarItem href="#" leadingIcon={<IconHome size={20} weight="regular" />}>Overview</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconChartLine size={20} weight="regular" />}>Reports</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconSettings size={20} weight="regular" />}>Settings</SidebarItem>
</SidebarSection>
<SidebarSection label="Account">
<SidebarItem href="#" leadingIcon={<IconUser size={20} weight="regular" />}>Profile</SidebarItem>
</SidebarSection>
</SidebarContent>
</Sidebar>
</SidebarProvider>side
Set on SidebarProvider. The edge the sidebar sits on; the Sheet opens from the same edge.
import { IconChartLine, IconHome, IconSettings, IconUser } from "@tabler/icons-react"
import { Sidebar, SidebarContent, SidebarItem, SidebarProvider, SidebarSection, SidebarTrigger } from "@/components/ui/sidebar"
<SidebarProvider>
<SidebarTrigger label="Toggle navigation" />
<Sidebar>
<SidebarContent>
<SidebarSection label="Workspace">
<SidebarItem href="#" leadingIcon={<IconHome size={20} weight="regular" />}>Overview</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconChartLine size={20} weight="regular" />}>Reports</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconSettings size={20} weight="regular" />}>Settings</SidebarItem>
</SidebarSection>
<SidebarSection label="Account">
<SidebarItem href="#" leadingIcon={<IconUser size={20} weight="regular" />}>Profile</SidebarItem>
</SidebarSection>
</SidebarContent>
</Sidebar>
</SidebarProvider>
<SidebarProvider side="right">
<SidebarTrigger label="Toggle navigation" />
<Sidebar>
<SidebarContent>
<SidebarSection label="Workspace">
<SidebarItem href="#" leadingIcon={<IconHome size={20} weight="regular" />}>Overview</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconChartLine size={20} weight="regular" />}>Reports</SidebarItem>
<SidebarItem href="#" leadingIcon={<IconSettings size={20} weight="regular" />}>Settings</SidebarItem>
</SidebarSection>
<SidebarSection label="Account">
<SidebarItem href="#" leadingIcon={<IconUser size={20} weight="regular" />}>Profile</SidebarItem>
</SidebarSection>
</SidebarContent>
</Sidebar>
</SidebarProvider>Examples
App navigation
import { IconChartLine, IconHome, IconSettings } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarItem, SidebarProvider, SidebarSection, SidebarTrigger } from "@/components/ui/sidebar"
<SidebarProvider>
<SidebarTrigger label="Toggle navigation" />
<Sidebar>
<SidebarHeader>
<p className="px-sm font-sans text-md font-medium text-text-primary">Acme</p>
</SidebarHeader>
<SidebarContent>
<SidebarSection label="Workspace">
<SidebarItem active leadingIcon={<IconHome size={20} />} href="#">Overview</SidebarItem>
<SidebarItem leadingIcon={<IconChartLine size={20} />} href="#">Reports</SidebarItem>
</SidebarSection>
<SidebarSection label="Account">
<SidebarItem leadingIcon={<IconSettings size={20} />} count={2} href="#">Settings</SidebarItem>
</SidebarSection>
</SidebarContent>
<SidebarFooter>
<Button variant="secondary" className="w-full">Invite people</Button>
</SidebarFooter>
</Sidebar>
</SidebarProvider>Offcanvas with a trigger
collapsible offcanvas hides the sidebar on desktop; the trigger toggles it and opens the Sheet on a small screen.
import { IconLayoutGrid, IconLayoutGridFilled } from "@tabler/icons-react"
import { Sidebar, SidebarContent, SidebarItem, SidebarProvider, SidebarSection, SidebarTrigger } from "@/components/ui/sidebar"
<SidebarProvider collapsible="offcanvas" defaultCollapsed>
<SidebarTrigger label="Toggle navigation" />
<Sidebar>
<SidebarContent>
<SidebarSection label="Menu">
<SidebarItem leadingIcon={<IconLayoutGrid size={20} />} leadingIconOn={<IconLayoutGridFilled size={20} />} active>Projects</SidebarItem>
</SidebarSection>
</SidebarContent>
</Sidebar>
</SidebarProvider>Do and don't
Do
- Give every section a label; it names the nav and orients the reader.
- Mark exactly one item active, the page the user is on.
- Keep the footer for identity and account actions, not navigation.
Don't
- Don't switch between views of the same content; use Tabs.
- Don't put controls other than items inside the content; put them in the header or footer.
- Don't nest a sidebar inside a sidebar.
Props
| Name | Type | Values | Default | Description |
|---|---|---|---|---|
| collapsed | boolean | - | - | Set on SidebarProvider. The collapsed state, when you control it. Pair it with onCollapsedChange. |
| defaultCollapsed | boolean | - | false | Set on SidebarProvider. Collapsed on first render. |
| onCollapsedChange | function | - | - | Set on SidebarProvider. Called with the next collapsed state. |
| collapsible | enum | icon | offcanvas | none | icon | Set on SidebarProvider. icon keeps a rail of icons with Tooltips when collapsed; offcanvas hides the sidebar on desktop entirely; none fixes it open. |
| side | enum | left | right | left | Set on SidebarProvider. The edge the sidebar sits on; the Sheet opens from the same edge. |
| label | string | - | Main | Set on SidebarContent, the name of its navigation landmark; set on SidebarSection, the group's heading and name (hidden on the icon rail). |
| active | boolean | - | false | Set on SidebarItem. The page this item points to: aria-current="page", the muted accent fill with its strong text, and the fill icon weight. |
| asChild | boolean | - | false | Set on SidebarItem. Renders the item as the single child element, typically a router link. |
| href | string | - | - | Set on SidebarItem. With it the item is a link; without it a button. |
| count | number | - | - | Set on SidebarItem. A number after the label in a Badge; on the rail it moves into the Tooltip. |
| leadingIcon | node | - | - | Set on SidebarItem. The Tabler icon before the label, 20px. |
| leadingIconOn | node | - | - | Set on SidebarItem. The filled-variant icon (Tabler *Filled, e.g. IconHomeFilled) shown on the active item instead of leadingIcon. |
| disabled | boolean | - | false | Set on SidebarItem. The item cannot activate and dims, including links and slotted router links. Disabled items leave the tab order; on the icon rail their Tooltip still opens on hover. |
Slots
| Name | Required | Description |
|---|---|---|
| header | no | SidebarHeader above the scrolling content. |
| content | yes | SidebarContent with one or more SidebarSection of SidebarItem. |
| footer | no | SidebarFooter pinned at the end. |
Anatomy
| Part | Description |
|---|---|
| root | Sidebar: the desktop column, hidden below md, 256px wide, bordered only on the edge that faces the content. On the icon rail it is one square md item wide plus spacing-sm on each side (49px with the border on mouse, 53px on touch), and the header, content and footer pad with spacing-sm. |
| header | SidebarHeader: the block above the scrolling content, such as the workspace name. |
| content | SidebarContent: the scrolling group of sections, a nav named by its label (default "Main"). |
| footer | SidebarFooter: the block pinned at the end, with a Separator above it. |
| section | SidebarSection: a group of related items, named by its label, with the items in a list. |
| label | The section heading: 12px medium secondary text, hidden on the icon rail. |
| item | SidebarItem: one destination, a link with href, a button without one, or any element with asChild. At least the md control height, 14px medium text, a Badge after the label with count. On the icon rail an item with an icon is a square of the md control height. |
| current | The active item, the page the user is on: the muted accent fill with its strong text. |
| currentIcon | The active item's icon in the accent icon color, at the fill weight. |
| icon | The item's leading Tabler icon at 20px; leadingIconOn (the *Filled variant) replaces it on the active item. |
| trigger | SidebarTrigger: one ghost icon button that collapses the sidebar on md and up and opens the Sheet below it, named by its label. With collapsible none it shows only below md. |
Accessibility
Role: complementary
| Keys | Action |
|---|---|
| Tab | Moves from item to item; items are links or buttons in the page order. |
| Enter, Space | A button item activates; a link item follows its href. |
| Escape | Closes the Sheet the sidebar becomes on a small screen. |
- The root is an aside (complementary). SidebarContent inside it is the one navigation landmark, named by its label (default "Main").
- Each SidebarSection is a group named by its visible label (aria-labelledby), or by aria-label on the icon rail where the label is hidden. Its items are a list.
- The current page's item sets aria-current="page" and its icon switches to the fill weight; the state never relies on color alone.
- On the rail an item with an icon moves its label into a Tooltip and an sr-only span. Items without icons keep visible text. A disabled item stays out of the tab order, and its Tooltip still opens on hover. Mobile sheets always show full labels.
- The Sheet names itself with an sr-only title; the trigger button carries the same name in aria-label.
- Sidebar parts must sit inside a SidebarProvider; without it they throw.
Install
npx shadcn add https://werewolf-ui.vercel.app/r/sidebar.jsonThis also adds styles/werewolf.css, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after @import "tailwindcss", with a path relative to that CSS file (from app/globals.css it is @import "../styles/werewolf.css").