Sidebar

A composable, responsive container for application navigation and supporting content.

Application
	<script lang="ts">
  import { Dialog, Sidebar } from "bits-ui";
  import Calendar from "phosphor-svelte/lib/Calendar";
  import Gear from "phosphor-svelte/lib/Gear";
  import House from "phosphor-svelte/lib/House";
  import MagnifyingGlass from "phosphor-svelte/lib/MagnifyingGlass";
  import SidebarIcon from "phosphor-svelte/lib/Sidebar";
  import Tray from "phosphor-svelte/lib/Tray";
  import X from "phosphor-svelte/lib/X";
 
  let openMobile = $state(false);
 
  const items = [
    { title: "Home", icon: House },
    { title: "Inbox", icon: Tray },
    { title: "Calendar", icon: Calendar },
    { title: "Search", icon: MagnifyingGlass },
    { title: "Settings", icon: Gear }
  ];
</script>
 
{#snippet sidebarPanel(panelState: Sidebar.SidebarState, mobile: boolean)}
  <Sidebar.Root
    collapsible="icon"
    class="group/sidebar relative flex h-full shrink-0 flex-col border-r transition-[width] duration-200 ease-linear motion-reduce:transition-none {mobile
      ? 'bg-background w-[18rem]'
      : panelState === 'expanded'
        ? 'bg-muted/45 w-64'
        : 'bg-muted/45 w-12'}"
  >
    <Sidebar.Content
      class="min-h-0 flex-1 overflow-y-auto overflow-x-hidden group-data-[state=collapsed]/sidebar:overflow-hidden"
    >
      <Sidebar.Group class="p-2">
        <Sidebar.GroupLabel
          class="text-foreground-alt mb-1 flex h-8 items-center whitespace-nowrap px-2 text-xs transition-[margin,opacity] duration-200 ease-linear group-data-[state=collapsed]/sidebar:-mt-9 group-data-[state=collapsed]/sidebar:opacity-0 motion-reduce:transition-none"
        >
          Application
        </Sidebar.GroupLabel>
        <Sidebar.GroupContent>
          <Sidebar.Menu class="space-y-1">
            {#each items as item (item.title)}
              {@const Icon = item.icon}
              <Sidebar.MenuItem>
                <Sidebar.MenuButton
                  aria-label={item.title}
                  title={panelState === "collapsed" && !mobile
                    ? item.title
                    : undefined}
                  class="hover:bg-muted focus-visible:ring-foreground flex h-8 w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm focus-visible:outline-none focus-visible:ring-2"
                >
                  <Icon class="size-4 shrink-0" />
                  <span
                    class="truncate whitespace-nowrap transition-opacity duration-100 ease-linear group-data-[state=collapsed]/sidebar:opacity-0 motion-reduce:transition-none"
                  >
                    {item.title}
                  </span>
                </Sidebar.MenuButton>
              </Sidebar.MenuItem>
            {/each}
          </Sidebar.Menu>
        </Sidebar.GroupContent>
      </Sidebar.Group>
    </Sidebar.Content>
 
    {#if !mobile}
      <Sidebar.Rail
        class="hover:bg-foreground/10 focus-visible:bg-foreground/10 absolute inset-y-0 -right-1 z-10 w-2 cursor-ew-resize focus-visible:outline-none"
        aria-label="Toggle sidebar"
      />
    {/if}
  </Sidebar.Root>
{/snippet}
 
<Sidebar.Provider
  bind:openMobile
  class="bg-background relative flex h-[420px] w-full overflow-hidden rounded-lg border"
>
  {#snippet children({ state, isMobile })}
    {#if isMobile}
      <Dialog.Root bind:open={openMobile}>
        <Dialog.Portal>
          <Dialog.Overlay
            class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 fixed inset-0 z-50 bg-black/45 motion-reduce:animate-none"
          />
          <Dialog.Content
            class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:slide-in-from-left data-[state=closed]:slide-out-to-left fixed inset-y-0 left-0 z-50 outline-none motion-reduce:animate-none"
          >
            <Dialog.Title class="sr-only">Application navigation</Dialog.Title>
            <Dialog.Description class="sr-only">
              Navigate to another section of the application.
            </Dialog.Description>
            <Dialog.Close
              class="hover:bg-muted focus-visible:ring-foreground absolute right-2 top-2 z-10 inline-flex size-8 items-center justify-center rounded-md focus-visible:outline-none focus-visible:ring-2"
              aria-label="Close sidebar"
            >
              <X class="size-4" />
            </Dialog.Close>
            {@render sidebarPanel(state, true)}
          </Dialog.Content>
        </Dialog.Portal>
      </Dialog.Root>
    {:else}
      {@render sidebarPanel(state, false)}
    {/if}
 
    <Sidebar.Inset class="bg-background min-w-0 flex-1">
      <header class="flex h-12 items-center px-4">
        <Sidebar.Trigger
          class="hover:bg-muted focus-visible:ring-foreground inline-flex size-8 items-center justify-center rounded-md focus-visible:outline-none focus-visible:ring-2"
          aria-label="Toggle sidebar"
        >
          <SidebarIcon class="size-4" />
        </Sidebar.Trigger>
      </header>
    </Sidebar.Inset>
  {/snippet}
</Sidebar.Provider>

Overview

The Sidebar primitive provides state, responsive behavior, keyboard controls, semantics, and data attributes for building application sidebars. It is intentionally unstyled: use its parts to create a reusable sidebar that belongs to your product and design system.

The component follows a compound structure familiar from shadcn's Sidebar while keeping layout, mobile presentation, styling, and persistence in your control.

Key Features

  • Desktop and Mobile State: Independent open and openMobile values prevent one layout from unexpectedly changing the other.
  • Responsive Context: Automatically switches to mobile state at a configurable breakpoint, or accepts an explicit isMobile value.
  • Keyboard Shortcut: Command/Control + B toggles the active sidebar by default.
  • Accessible Controls: Trigger and Rail expose the active state through aria-expanded and reference the sidebar with aria-controls.
  • Composable Parts: Includes containers for headers, content, groups, menus, actions, badges, nested menus, skeletons, and inset content.
  • Styling Hooks: State and configuration are exposed through data attributes on each relevant part.

Structure

	<script lang="ts">
  import { Sidebar } from "bits-ui";
</script>
 
<Sidebar.Provider>
  <Sidebar.Root>
    <Sidebar.Header />
    <Sidebar.Content>
      <Sidebar.Group>
        <Sidebar.GroupLabel />
        <Sidebar.GroupAction />
        <Sidebar.GroupContent>
          <Sidebar.Menu>
            <Sidebar.MenuItem>
              <Sidebar.MenuButton />
              <Sidebar.MenuAction />
              <Sidebar.MenuBadge />
            </Sidebar.MenuItem>
          </Sidebar.Menu>
        </Sidebar.GroupContent>
      </Sidebar.Group>
    </Sidebar.Content>
    <Sidebar.Footer />
    <Sidebar.Rail />
  </Sidebar.Root>
 
  <Sidebar.Inset>
    <Sidebar.Trigger />
    <!-- page content -->
  </Sidebar.Inset>
</Sidebar.Provider>

Provider must wrap every sidebar part. Beyond that requirement, parts can be arranged to fit your layout.

Managing State

open controls the desktop sidebar and defaults to true. openMobile controls the mobile sidebar and defaults to false. Both are bindable.

	<script lang="ts">
  import { Sidebar } from "bits-ui";
 
  let open = $state(true);
  let openMobile = $state(false);
</script>
 
<Sidebar.Provider bind:open bind:openMobile>
  <!-- ... -->
</Sidebar.Provider>

Use onOpenChange and onOpenMobileChange when you need to react to changes. Set disabled on the provider to prevent all toggling.

Snippet State

The Provider and Root children snippets expose the current state and control functions. This is useful for conditional layout or composing a modal mobile sidebar.

	<Sidebar.Provider>
  {#snippet children({ state, isMobile, openMobile, setOpenMobile, toggle })}
    <p>{state}</p>
    <button onclick={toggle}>Toggle</button>
 
    {#if isMobile && openMobile}
      <!-- Render the sidebar in your Dialog or Sheet component. -->
      <button onclick={() => setOpenMobile(false)}>Close</button>
    {/if}
  {/snippet}
</Sidebar.Provider>

For an accessible modal mobile experience, compose the mobile Root with the Bits UI Dialog primitive, as shown in the demo. This adds managed focus, Escape handling, and outside-interaction behavior without coupling the Sidebar primitive to a particular presentation.

Responsive Behavior

When isMobile is not provided, the provider watches the viewport and uses mobile state below mobileBreakpoint (768 pixels by default).

	<Sidebar.Provider mobileBreakpoint={900}>
  <!-- ... -->
</Sidebar.Provider>

Pass isMobile when your application already has a responsive-state source or when the sidebar lives in a container whose behavior does not match the viewport.

Collapse Modes and Variants

Root exposes configuration as data attributes so your styles can implement the desired layout:

  • collapsible="offcanvas" hides the sidebar outside the viewport when collapsed.
  • collapsible="icon" reduces the sidebar to an icon-width rail when collapsed.
  • collapsible="none" keeps it expanded and disables Trigger and Rail toggling.
  • variant="sidebar", "floating", or "inset" describes the visual relationship to the page.
  • side="left" or "right" describes its physical position.

The primitive does not set widths or transitions. This keeps values such as the expanded width, collapsed rail width, and animation curve in your CSS.

	[data-sidebar-root] {
  width: 16rem;
  overflow: hidden;
  transition: width 200ms linear;
}
 
[data-sidebar-root][data-collapsible="icon"][data-state="collapsed"] {
  width: 3rem;
}
 
@media (prefers-reduced-motion: reduce) {
  [data-sidebar-root] {
    transition: none;
  }
}

Keep sidebar content mounted during the width transition. Hide or clip labels with data-state styles instead of conditionally removing them, which avoids a second layout change while the panel is moving.

Keyboard Shortcut

The default shortcut is Command/Control + B. Change its key with keyboardShortcut, or set it to null to disable the shortcut.

	<Sidebar.Provider keyboardShortcut="s">
  <!-- Command/Control + S -->
</Sidebar.Provider>

The shortcut is ignored when Shift or Alt is held, when the event has already been prevented, or when sidebar toggling is disabled.

Active Menu Items

Use isActive on MenuButton and MenuSubButton. It adds data-active and aria-current="page", which you can target in your styles.

	<Sidebar.MenuButton
  isActive={pathname === "/dashboard"}
  class="data-[active]:bg-accent"
>
  Dashboard
</Sidebar.MenuButton>

MenuButton also exposes data-size and data-variant; MenuAction exposes data-show-on-hover. These attributes describe styling intent without supplying styles.

Programmatic Access

Call useSidebar in a component rendered inside Provider to access the same state and controls without passing snippet props through intermediate components.

SidebarStatus.svelte
	<script lang="ts">
  import { Sidebar } from "bits-ui";
 
  const sidebar = Sidebar.useSidebar();
</script>
 
<button onclick={sidebar.toggle}>
  Sidebar is {sidebar.state}
</button>

Persistence

State persistence is deliberately left to the application. Bind open and store it in a cookie, local storage, or your own state layer when persistence is appropriate. The primitive does not write cookies or access storage on its own.

API Reference

Sidebar.Provider

Provides responsive open state and controls to every sidebar part.

Property Details
open
onOpenChange
openMobile
onOpenMobileChange
disabled
isMobile
mobileBreakpoint
keyboardShortcut
ref
children
child
Data Attribute Details
data-state
data-mobile
data-disabled
data-sidebar-provider

Sidebar.Root

The main sidebar container.

Property Details
side
variant
collapsible
ref
children
child
Data Attribute Details
data-state
data-mobile
data-side
data-variant
data-collapsible
data-sidebar-root

Sidebar.Trigger

A button that toggles the active desktop or mobile sidebar state.

Property Details
disabled
ref
children
child
Data Attribute Details
data-state
data-mobile
data-disabled
data-sidebar-trigger

Sidebar.Rail

A secondary, pointer-oriented button for toggling the sidebar from its edge.

Property Details
disabled
ref
children
child
Data Attribute Details
data-state
data-mobile
data-disabled
data-sidebar-rail

Sidebar.Inset

The main content container used alongside the inset sidebar variant.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-inset

Sidebar.Input

An input styled by consumers for sidebar search or filtering.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-input

A container for persistent content at the top of the sidebar.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-header

A container for persistent content at the bottom of the sidebar.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-footer

Sidebar.Separator

A horizontal separator between sidebar regions.

Property Details
decorative
ref
children
child
Data Attribute Details
data-sidebar-separator

Sidebar.Content

The primary scrollable area between the sidebar header and footer.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-content

Sidebar.Group

Groups related sidebar navigation and actions.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-group

Sidebar.GroupLabel

A visible label for a sidebar group.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-group-label

Sidebar.GroupAction

A button for an action associated with a sidebar group.

Property Details
disabled
ref
children
child
Data Attribute Details
data-disabled
data-sidebar-group-action

Sidebar.GroupContent

The content container for a sidebar group.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-group-content

A semantic list of sidebar menu items.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-menu

A semantic sidebar menu list item.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-menu-item

A sidebar menu button with active, size, and style-variant states.

Property Details
isActive
size
variant
disabled
ref
children
child
Data Attribute Details
data-active
data-disabled
data-size
data-variant
data-sidebar-menu-button

A secondary action associated with a sidebar menu item.

Property Details
disabled
showOnHover
ref
children
child
Data Attribute Details
data-disabled
data-show-on-hover
data-sidebar-menu-action

A badge associated with a sidebar menu item.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-menu-badge

A presentation container for a loading menu item.

Property Details
showIcon
ref
children
child
Data Attribute Details
data-icon
data-sidebar-menu-skeleton

A semantic nested list within a sidebar menu item.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-menu-sub

A semantic item in a nested sidebar menu.

Property Details
ref
children
child
Data Attribute Details
data-sidebar-menu-sub-item

A link for a nested sidebar menu destination.

Property Details
isActive
size
ref
children
child
Data Attribute Details
data-active
data-size
data-sidebar-menu-sub-button