Kumo Svelte
  • Home
  • Installation
  • Colors
  • Accessibility
  • Changelog
  • Autocomplete
  • Badge
  • Banner
  • Breadcrumbs
  • Button
  • Checkbox
  • Clipboard Text
  • Cloudflare Logo
  • CodeHighlighted
  • Collapsible
  • Combobox
  • Command Palette
  • Date Picker
  • Dialog
  • Dropdown
  • Empty
  • Flow
  • Grid
  • Input
  • InputArea
  • InputGroup
  • Label
  • Layer Card
  • Link
  • Loader
  • MenuBar
  • Meter
  • Pagination
  • Popover
  • Radio
  • Select
  • Sensitive Input
  • Sidebar
  • Skeleton Line
  • Switch
  • Table
  • Table of Contents
  • Tabs
  • Text
  • Toast
  • Toolbar
  • Tooltip
  • Charts
  • Colors
  • Timeseries
  • Maps
  • Sankey
  • Custom Chart
  • Page Header
  • Resource List
  • Delete Resource
Sidebar
kumo-svelte

Sidebar

A composable sidebar navigation component with collapsible groups, icon-only mode, sliding views, and responsive mobile support.

Build
Protect & Connect
example.com
Main content area
<script lang="ts">
  import ArrowLeftIcon from "phosphor-svelte/lib/ArrowLeftIcon";
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import CodeIcon from "phosphor-svelte/lib/CodeIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import GlobeIcon from "phosphor-svelte/lib/GlobeIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import LockIcon from "phosphor-svelte/lib/LockIcon";
  import MagnifyingGlassIcon from "phosphor-svelte/lib/MagnifyingGlassIcon";
  import ShieldCheckIcon from "phosphor-svelte/lib/ShieldCheckIcon";
  import type { Component } from "svelte";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import AccountSwitcher from "./sidebar-account-switcher.svelte";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    badge?: string;
    class?: string;
    icon: Component;
    label: string;
    onSelect?: () => void;
    tooltip?: string;
  }

  interface SubMenuItem {
    badge?: string;
    label: string;
  }

  interface NestedSubMenuItem extends SubMenuItem {
    children: SubMenuItem[];
  }

  interface CollapsibleMenuItem extends MenuItem {
    children: Array<NestedSubMenuItem | SubMenuItem>;
  }

  interface MenuSection {
    items: Array<CollapsibleMenuItem | MenuItem>;
    label?: string;
  }

  let surface = $state<"account" | "domain">("account");

  function showDomainNavigation() {
    surface = "domain";
  }

  function showAccountNavigation() {
    surface = "account";
  }

  const sidebarNavigation = {
    account: {
      search: {
        label: "Quick search…",
        icon: MagnifyingGlassIcon,
        tooltip: "Search",
        class:
          "mb-3 ring ring-kumo-line transition-[margin] duration-250 group-data-[state=collapsed]/sidebar:mb-0 group-data-[state=collapsed]/sidebar:ring-transparent",
      } satisfies MenuItem,
      sections: [
        {
          items: [
            { label: "Home", icon: HouseIcon, active: true },
            { label: "Analytics & Logs", icon: ChartBarIcon },
            { label: "Domains", icon: GlobeIcon, onSelect: showDomainNavigation },
          ],
        },
        {
          label: "Build",
          items: [
            {
              label: "Compute",
              icon: CodeIcon,
              children: [
                {
                  label: "Workers & Pages",
                  children: [{ label: "Overview" }, { label: "Workers" }, { label: "Pages" }],
                },
                { label: "Durable Objects" },
                { label: "Containers", badge: "Beta" },
              ],
            },
            { label: "Storage", icon: DatabaseIcon },
          ],
        },
        {
          label: "Protect & Connect",
          items: [
            { label: "Security", icon: ShieldCheckIcon },
            { label: "Zero Trust", icon: LockIcon, badge: "Beta" },
          ],
        },
      ] satisfies MenuSection[],
    },
    domain: {
      sections: [
        {
          items: [{ label: "Back", icon: ArrowLeftIcon, onSelect: showAccountNavigation }],
        },
        {
          label: "example.com",
          items: [
            { label: "Overview", icon: GlobeIcon, active: true },
            { label: "Security", icon: ShieldCheckIcon },
            { label: "SSL/TLS", icon: LockIcon },
            { label: "Analytics", icon: ChartBarIcon },
            { label: "Caching", icon: DatabaseIcon },
          ],
        },
      ] satisfies MenuSection[],
    },
  };

  function hasChildren(item: CollapsibleMenuItem | MenuItem): item is CollapsibleMenuItem {
    return "children" in item;
  }

  function hasNestedChildren(item: NestedSubMenuItem | SubMenuItem): item is NestedSubMenuItem {
    return "children" in item;
  }
</script>

{#snippet menuButton(item: MenuItem)}
  {@const Icon = item.icon}
  <Sidebar.MenuButton active={item.active} onclick={item.onSelect} tooltip={item.tooltip} class={item.class}>
    {#snippet icon()}<Icon />{/snippet}
    {item.label}
    {#if item.badge}<Sidebar.MenuBadge>{item.badge}</Sidebar.MenuBadge>{/if}
  </Sidebar.MenuButton>
{/snippet}

{#snippet subMenuButton(item: SubMenuItem)}
  <Sidebar.MenuSubButton>
    {item.label}
    {#if item.badge}<Sidebar.MenuBadge>{item.badge}</Sidebar.MenuBadge>{/if}
  </Sidebar.MenuSubButton>
{/snippet}

{#snippet nestedSubMenu(item: NestedSubMenuItem)}
  <Sidebar.MenuSubItem>
    <Sidebar.Collapsible>
      <Sidebar.CollapsibleTrigger>
        {#snippet child({ props })}
          <Sidebar.MenuSubButton {...props}>
            {item.label} <Sidebar.MenuChevron />
          </Sidebar.MenuSubButton>
        {/snippet}
      </Sidebar.CollapsibleTrigger>
      <Sidebar.CollapsibleContent>
        <Sidebar.MenuSub>
          {#each item.children as child (child.label)}
            {@render subMenuButton(child)}
          {/each}
        </Sidebar.MenuSub>
      </Sidebar.CollapsibleContent>
    </Sidebar.Collapsible>
  </Sidebar.MenuSubItem>
{/snippet}

{#snippet collapsibleMenuButton(item: CollapsibleMenuItem)}
  <Sidebar.MenuItem>
    <Sidebar.Collapsible open>
      <Sidebar.CollapsibleTrigger>
        {#snippet child({ props })}
          {@const Icon = item.icon}
          <Sidebar.MenuButton {...props}>
            {#snippet icon()}<Icon />{/snippet}
            {item.label} <Sidebar.MenuChevron />
          </Sidebar.MenuButton>
        {/snippet}
      </Sidebar.CollapsibleTrigger>
      <Sidebar.CollapsibleContent>
        <Sidebar.MenuSub>
          {#each item.children as child (child.label)}
            {#if hasNestedChildren(child)}
              {@render nestedSubMenu(child)}
            {:else}
              {@render subMenuButton(child)}
            {/if}
          {/each}
        </Sidebar.MenuSub>
      </Sidebar.CollapsibleContent>
    </Sidebar.Collapsible>
  </Sidebar.MenuItem>
{/snippet}

{#snippet menuSection(section: MenuSection)}
  <Sidebar.Group>
    {#if section.label}
      <Sidebar.GroupLabel>{section.label}</Sidebar.GroupLabel>
    {/if}
    <Sidebar.Menu>
      {#each section.items as item (item.label)}
        {#if hasChildren(item)}
          {@render collapsibleMenuButton(item)}
        {:else}
          {@render menuButton(item)}
        {/if}
      {/each}
    </Sidebar.Menu>
  </Sidebar.Group>
{/snippet}

<DemoShell>
  <Sidebar.Provider contained defaultOpen peekable class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Header>
        <AccountSwitcher />
      </Sidebar.Header>

      <Sidebar.SlidingViews activeKey={surface} direction={surface === "domain" ? "left" : "right"}>
        <Sidebar.SlidingView value="account">
          <Sidebar.Content>
            <Sidebar.Group>
              <Sidebar.Menu>
                {@render menuButton(sidebarNavigation.account.search)}
              </Sidebar.Menu>
            </Sidebar.Group>

            {#each sidebarNavigation.account.sections as section (section.label ?? section.items[0]?.label)}
              {@render menuSection(section)}
            {/each}
          </Sidebar.Content>
        </Sidebar.SlidingView>

        <Sidebar.SlidingView value="domain">
          <Sidebar.Content>
            {#each sidebarNavigation.domain.sections as section (section.label ?? section.items[0]?.label)}
              {@render menuSection(section)}
            {/each}
          </Sidebar.Content>
        </Sidebar.SlidingView>
      </Sidebar.SlidingViews>

      <Sidebar.Footer>
        <Sidebar.Trigger />
      </Sidebar.Footer>
    </Sidebar.Root>
    <DemoMain />
  </Sidebar.Provider>
</DemoShell>

Import

import * as Sidebar from "kumo-svelte/components/sidebar";

Usage

At minimum you need Sidebar.Provider, Sidebar.Root, Sidebar.Content, Sidebar.Menu, and Sidebar.MenuButton. Add Sidebar.Header and Sidebar.Footer to pin content above or below the scroll area. Use Sidebar.Group and Sidebar.GroupLabel to organize sections.

<script lang="ts">
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import * as Sidebar from "kumo-svelte/components/sidebar";
</script>

<Sidebar.Provider defaultOpen>
  <Sidebar.Root>
    <Sidebar.Content>
      <Sidebar.Group>
        <Sidebar.GroupLabel>Navigation</Sidebar.GroupLabel>
        <Sidebar.Menu>
          <Sidebar.MenuButton active>
            {#snippet icon()}<HouseIcon />{/snippet}
            Home
          </Sidebar.MenuButton>
        </Sidebar.Menu>
      </Sidebar.Group>
    </Sidebar.Content>
  </Sidebar.Root>
  <main class="flex-1"><slot /></main>
</Sidebar.Provider>

Examples

Basic

The minimum viable sidebar: just groups, menu buttons, and collapsible sub-menus.

Overview
Build
Main content area
<script lang="ts">
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import CodeIcon from "phosphor-svelte/lib/CodeIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import GlobeIcon from "phosphor-svelte/lib/GlobeIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import type { Component } from "svelte";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    icon: Component;
    label: string;
  }

  const overviewItems: MenuItem[] = [
    { label: "Home", icon: HouseIcon, active: true },
    { label: "Analytics", icon: ChartBarIcon },
    { label: "Domains", icon: GlobeIcon },
  ];

  const computeItem = { label: "Compute", icon: CodeIcon } satisfies MenuItem;
  const buildItems: MenuItem[] = [{ label: "Storage", icon: DatabaseIcon }];
  const workersPagesItems = ["Overview", "Workers", "Pages"];
  const computeItems = ["Durable Objects"];
</script>

<DemoShell>
  <Sidebar.Provider contained defaultOpen class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Content>
        <Sidebar.Group>
          <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
          <Sidebar.Menu>
            {#each overviewItems as item (item.label)}
              {@const Icon = item.icon}
              <Sidebar.MenuButton active={item.active}>
                {#snippet icon()}<Icon />{/snippet}
                {item.label}
              </Sidebar.MenuButton>
            {/each}
          </Sidebar.Menu>
        </Sidebar.Group>

        <Sidebar.Group>
          <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>
          <Sidebar.Menu>
            <Sidebar.MenuItem>
              <Sidebar.Collapsible open>
                <Sidebar.CollapsibleTrigger>
                  {#snippet child({ props })}
                    {@const Icon = computeItem.icon}
                    <Sidebar.MenuButton {...props}>
                      {#snippet icon()}<Icon />{/snippet}
                      {computeItem.label} <Sidebar.MenuChevron />
                    </Sidebar.MenuButton>
                  {/snippet}
                </Sidebar.CollapsibleTrigger>
                <Sidebar.CollapsibleContent>
                  <Sidebar.MenuSub>
                    <Sidebar.MenuSubItem>
                      <Sidebar.Collapsible>
                        <Sidebar.CollapsibleTrigger>
                          {#snippet child({ props })}
                            <Sidebar.MenuSubButton {...props}>
                              Workers & Pages <Sidebar.MenuChevron />
                            </Sidebar.MenuSubButton>
                          {/snippet}
                        </Sidebar.CollapsibleTrigger>
                        <Sidebar.CollapsibleContent>
                          <Sidebar.MenuSub>
                            {#each workersPagesItems as label (label)}
                              <Sidebar.MenuSubButton>{label}</Sidebar.MenuSubButton>
                            {/each}
                          </Sidebar.MenuSub>
                        </Sidebar.CollapsibleContent>
                      </Sidebar.Collapsible>
                    </Sidebar.MenuSubItem>
                    {#each computeItems as label (label)}
                      <Sidebar.MenuSubButton>{label}</Sidebar.MenuSubButton>
                    {/each}
                  </Sidebar.MenuSub>
                </Sidebar.CollapsibleContent>
              </Sidebar.Collapsible>
            </Sidebar.MenuItem>

            {#each buildItems as item (item.label)}
              {@const Icon = item.icon}
              <Sidebar.MenuButton active={item.active}>
                {#snippet icon()}<Icon />{/snippet}
                {item.label}
              </Sidebar.MenuButton>
            {/each}
          </Sidebar.Menu>
        </Sidebar.Group>
      </Sidebar.Content>
    </Sidebar.Root>
    <DemoMain />
  </Sidebar.Provider>
</DemoShell>

Toggle & Collapsed State

Use Sidebar.Trigger in the footer or useSidebar().toggleSidebar programmatically. Pass tooltip to show labels on hover when collapsed.

Company

Click the button or the sidebar trigger to toggle

<script lang="ts">
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import CodeIcon from "phosphor-svelte/lib/CodeIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import type { Component } from "svelte";
  import { useSidebar } from "kumo-svelte/components/sidebar";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import BrandLogo from "./sidebar-brand-logo.svelte";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    icon: Component;
    label: string;
    tooltip: string;
  }

  const menuItems: MenuItem[] = [
    { label: "Home", tooltip: "Home", icon: HouseIcon, active: true },
    { label: "Analytics", tooltip: "Analytics", icon: ChartBarIcon },
    { label: "Compute", tooltip: "Compute", icon: CodeIcon },
    { label: "Storage", tooltip: "Storage", icon: DatabaseIcon },
  ];
</script>

{#snippet toggleButton()}
  {@const sidebar = useSidebar("ToggleButton")}
  <button
    type="button"
    onclick={sidebar.toggleSidebar}
    class="rounded-lg border border-kumo-hairline bg-kumo-base px-3 py-1.5 text-sm text-kumo-default transition-colors hover:bg-kumo-tint"
  >
    {sidebar.state === "expanded" ? "Collapse" : "Expand"}
  </button>
{/snippet}

<DemoShell>
  <Sidebar.Provider contained defaultOpen class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Header>
        <BrandLogo />
      </Sidebar.Header>
      <Sidebar.Content>
        <Sidebar.Group>
          <Sidebar.Menu>
            {#each menuItems as item (item.label)}
              {@const Icon = item.icon}
              <Sidebar.MenuButton active={item.active} tooltip={item.tooltip}>
                {#snippet icon()}<Icon />{/snippet}
                {item.label}
              </Sidebar.MenuButton>
            {/each}
          </Sidebar.Menu>
        </Sidebar.Group>
      </Sidebar.Content>
      <Sidebar.Footer>
        <Sidebar.Trigger />
      </Sidebar.Footer>
    </Sidebar.Root>
    <DemoMain>
      {@render toggleButton()}
      <p class="text-sm">Click the button or the sidebar trigger to toggle</p>
    </DemoMain>
  </Sidebar.Provider>
</DemoShell>

Resizable

Drag the edge to resize. Dragging below minWidth collapses; dragging outward from collapsed expands.

Company
Overview

Drag the sidebar edge to resize

<script lang="ts">
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import type { Component } from "svelte";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import BrandLogo from "./sidebar-brand-logo.svelte";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    icon: Component;
    label: string;
  }

  const menuItems: MenuItem[] = [
    { label: "Home", icon: HouseIcon, active: true },
    { label: "Analytics", icon: ChartBarIcon },
    { label: "Storage", icon: DatabaseIcon },
  ];
</script>

<DemoShell>
  <Sidebar.Provider contained defaultOpen resizable defaultWidth={240} minWidth={180} maxWidth={400} class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Header>
        <BrandLogo />
      </Sidebar.Header>
      <Sidebar.Content>
        <Sidebar.Group>
          <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
          <Sidebar.Menu>
            {#each menuItems as item (item.label)}
              {@const Icon = item.icon}
              <Sidebar.MenuButton active={item.active}>
                {#snippet icon()}<Icon />{/snippet}
                {item.label}
              </Sidebar.MenuButton>
            {/each}
          </Sidebar.Menu>
        </Sidebar.Group>
      </Sidebar.Content>
      <Sidebar.Footer><Sidebar.Trigger /></Sidebar.Footer>
      <Sidebar.ResizeHandle />
    </Sidebar.Root>
    <DemoMain><p class="text-sm">Drag the sidebar edge to resize</p></DemoMain>
  </Sidebar.Provider>
</DemoShell>

Right Side

Use side="right" for a sidebar on the right edge. Place main content before Sidebar.Root in the DOM.

Main content area
Details
<script lang="ts">
  import BellIcon from "phosphor-svelte/lib/BellIcon";
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import GearIcon from "phosphor-svelte/lib/GearIcon";
  import type { Component } from "svelte";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    icon: Component;
    label: string;
  }

  const detailItems: MenuItem[] = [
    { label: "Properties", icon: GearIcon, active: true },
    { label: "Metrics", icon: ChartBarIcon },
    { label: "Alerts", icon: BellIcon },
  ];
</script>

<DemoShell>
  <Sidebar.Provider contained defaultOpen side="right" class="h-full min-h-0!">
    <DemoMain />
    <Sidebar.Root>
      <Sidebar.Content>
        <Sidebar.Group>
          <Sidebar.GroupLabel>Details</Sidebar.GroupLabel>
          <Sidebar.Menu>
            {#each detailItems as item (item.label)}
              {@const Icon = item.icon}
              <Sidebar.MenuButton active={item.active}>
                {#snippet icon()}<Icon />{/snippet}
                {item.label}
              </Sidebar.MenuButton>
            {/each}
          </Sidebar.Menu>
        </Sidebar.Group>
      </Sidebar.Content>
    </Sidebar.Root>
  </Sidebar.Provider>
</DemoShell>

Peeking

Set peekable to temporarily expand the collapsed sidebar on hover or focus.

Company
State: Expanded

Collapse, then hover the sidebar to peek

<script lang="ts">
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import CodeIcon from "phosphor-svelte/lib/CodeIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import type { Component } from "svelte";
  import { useSidebar } from "kumo-svelte/components/sidebar";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import BrandLogo from "./sidebar-brand-logo.svelte";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    icon: Component;
    label: string;
    tooltip: string;
  }

  const menuItems: MenuItem[] = [
    { label: "Home", tooltip: "Home", icon: HouseIcon, active: true },
    { label: "Analytics", tooltip: "Analytics", icon: ChartBarIcon },
    { label: "Compute", tooltip: "Compute", icon: CodeIcon },
    { label: "Storage", tooltip: "Storage", icon: DatabaseIcon },
  ];

  const labels = {
    collapsed: "Collapsed",
    expanded: "Expanded",
    peeking: "Peeking",
  };
</script>

{#snippet peekStateIndicator()}
  {@const sidebar = useSidebar("PeekStateIndicator")}
  <div class="flex flex-col items-center gap-2">
    <span class="font-medium text-kumo-default">State: {labels[sidebar.state]}</span>
    <p>Collapse, then hover the sidebar to peek</p>
  </div>
{/snippet}

<DemoShell>
  <Sidebar.Provider contained defaultOpen peekable class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Header>
        <BrandLogo />
      </Sidebar.Header>
      <Sidebar.Content>
        <Sidebar.Group>
          <Sidebar.Menu>
            {#each menuItems as item (item.label)}
              {@const Icon = item.icon}
              <Sidebar.MenuButton active={item.active} tooltip={item.tooltip}>
                {#snippet icon()}<Icon />{/snippet}
                {item.label}
              </Sidebar.MenuButton>
            {/each}
          </Sidebar.Menu>
        </Sidebar.Group>
      </Sidebar.Content>
      <Sidebar.Footer>
        <Sidebar.Trigger />
      </Sidebar.Footer>
    </Sidebar.Root>
    <DemoMain>
      {@render peekStateIndicator()}
    </DemoMain>
  </Sidebar.Provider>
</DemoShell>

Auto Scroll

Set autoScrollOnOpen on a nested Sidebar.Collapsible to keep newly revealed items in view.

Company
Overview
Platform
Build

Open Workers near the bottom of the list

<script lang="ts">
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import CodeIcon from "phosphor-svelte/lib/CodeIcon";
  import CubeIcon from "phosphor-svelte/lib/CubeIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import GearIcon from "phosphor-svelte/lib/GearIcon";
  import GlobeIcon from "phosphor-svelte/lib/GlobeIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import LockIcon from "phosphor-svelte/lib/LockIcon";
  import ShieldCheckIcon from "phosphor-svelte/lib/ShieldCheckIcon";
  import type { Component } from "svelte";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import BrandLogo from "./sidebar-brand-logo.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    badge?: string;
    icon: Component;
    label: string;
  }

  interface Section {
    items: MenuItem[];
    label: string;
  }

  const sections: Section[] = [
    {
      label: "Overview",
      items: [
        { label: "Home", icon: HouseIcon, active: true },
        { label: "Analytics", icon: ChartBarIcon },
        { label: "Domains", icon: GlobeIcon },
      ],
    },
    {
      label: "Platform",
      items: [
        { label: "Storage", icon: DatabaseIcon },
        { label: "Security", icon: ShieldCheckIcon },
        { label: "Zero Trust", icon: LockIcon },
        { label: "Settings", icon: GearIcon },
      ],
    },
  ];

  const workerItems = ["Overview", "Deployments", "Observability", "Settings"];
  const containersItem = { label: "Containers", icon: CubeIcon, badge: "Beta" } satisfies MenuItem;
</script>

<div class="relative h-[420px] w-full overflow-hidden rounded-lg border border-kumo-line bg-kumo-base">
  <Sidebar.Provider contained defaultOpen class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Header>
        <BrandLogo />
      </Sidebar.Header>
      <Sidebar.Content>
        {#each sections as section (section.label)}
          <Sidebar.Group>
            <Sidebar.GroupLabel>{section.label}</Sidebar.GroupLabel>
            <Sidebar.Menu>
              {#each section.items as item (item.label)}
                {@const Icon = item.icon}
                <Sidebar.MenuButton active={item.active}>
                  {#snippet icon()}<Icon />{/snippet}
                  {item.label}
                </Sidebar.MenuButton>
              {/each}
            </Sidebar.Menu>
          </Sidebar.Group>
        {/each}

        <Sidebar.Group>
          <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>
          <Sidebar.Menu>
            <Sidebar.MenuItem>
              <Sidebar.Collapsible autoScrollOnOpen>
                <Sidebar.CollapsibleTrigger>
                  {#snippet child({ props })}
                    <Sidebar.MenuButton {...props}>
                      {#snippet icon()}<CodeIcon />{/snippet}
                      Workers <Sidebar.MenuChevron />
                    </Sidebar.MenuButton>
                  {/snippet}
                </Sidebar.CollapsibleTrigger>
                <Sidebar.CollapsibleContent>
                  <Sidebar.MenuSub>
                    {#each workerItems as label (label)}
                      <Sidebar.MenuSubButton>{label}</Sidebar.MenuSubButton>
                    {/each}
                  </Sidebar.MenuSub>
                </Sidebar.CollapsibleContent>
              </Sidebar.Collapsible>
            </Sidebar.MenuItem>
            <Sidebar.MenuButton>
              {#snippet icon()}<CubeIcon />{/snippet}
              {containersItem.label}
              <Sidebar.MenuBadge>{containersItem.badge}</Sidebar.MenuBadge>
            </Sidebar.MenuButton>
          </Sidebar.Menu>
        </Sidebar.Group>
      </Sidebar.Content>
      <Sidebar.Footer>
        <Sidebar.Trigger />
      </Sidebar.Footer>
    </Sidebar.Root>
    <DemoMain>
      <p>Open Workers near the bottom of the list</p>
    </DemoMain>
  </Sidebar.Provider>
</div>

Sliding Views

Use Sidebar.SlidingViews and Sidebar.SlidingView to animate between navigation surfaces while keeping inactive views hidden from assistive technology and keyboard focus.

Account
Zone

Active: Account surface

Click the header button to slide between views

<script lang="ts">
  import ArrowsLeftRightIcon from "phosphor-svelte/lib/ArrowsLeftRightIcon";
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import GearIcon from "phosphor-svelte/lib/GearIcon";
  import GlobeIcon from "phosphor-svelte/lib/GlobeIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import LockIcon from "phosphor-svelte/lib/LockIcon";
  import ShieldCheckIcon from "phosphor-svelte/lib/ShieldCheckIcon";
  import UserIcon from "phosphor-svelte/lib/UserIcon";
  import type { Component } from "svelte";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  type SidebarSurface = "account" | "zone";

  interface MenuItem {
    active?: boolean;
    icon: Component;
    label: string;
  }

  const accountItems: MenuItem[] = [
    { label: "Home", icon: HouseIcon, active: true },
    { label: "Members", icon: UserIcon },
    { label: "Analytics", icon: ChartBarIcon },
    { label: "Settings", icon: GearIcon },
  ];

  const zoneItems: MenuItem[] = [
    { label: "Overview", icon: GlobeIcon, active: true },
    { label: "Security", icon: ShieldCheckIcon },
    { label: "SSL/TLS", icon: LockIcon },
    { label: "Caching", icon: DatabaseIcon },
  ];

  let surface = $state<SidebarSurface>("account");
</script>

<DemoShell>
  <Sidebar.Provider contained defaultOpen class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Header>
        <button
          type="button"
          onclick={() => (surface = surface === "account" ? "zone" : "account")}
          class="flex w-full cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-sm font-medium text-kumo-default transition-colors hover:bg-kumo-tint"
        >
          <ArrowsLeftRightIcon class="size-4 shrink-0 text-kumo-brand" />
          <span class="flex-1 text-left font-semibold text-kumo-strong">
            {surface === "account" ? "Account Nav" : "Zone Nav"}
          </span>
        </button>
      </Sidebar.Header>

      <Sidebar.SlidingViews activeKey={surface} direction={surface === "zone" ? "left" : "right"}>
        <Sidebar.SlidingView value="account">
          <Sidebar.Content>
            <Sidebar.Group>
              <Sidebar.GroupLabel>Account</Sidebar.GroupLabel>
              <Sidebar.Menu>
                {#each accountItems as item (item.label)}
                  {@const Icon = item.icon}
                  <Sidebar.MenuButton active={item.active}>
                    {#snippet icon()}<Icon />{/snippet}
                    {item.label}
                  </Sidebar.MenuButton>
                {/each}
              </Sidebar.Menu>
            </Sidebar.Group>
          </Sidebar.Content>
        </Sidebar.SlidingView>

        <Sidebar.SlidingView value="zone">
          <Sidebar.Content>
            <Sidebar.Group>
              <Sidebar.GroupLabel>Zone</Sidebar.GroupLabel>
              <Sidebar.Menu>
                {#each zoneItems as item (item.label)}
                  {@const Icon = item.icon}
                  <Sidebar.MenuButton active={item.active}>
                    {#snippet icon()}<Icon />{/snippet}
                    {item.label}
                  </Sidebar.MenuButton>
                {/each}
              </Sidebar.Menu>
            </Sidebar.Group>
          </Sidebar.Content>
        </Sidebar.SlidingView>
      </Sidebar.SlidingViews>
    </Sidebar.Root>

    <DemoMain>
      <div class="flex flex-col items-center gap-2">
        <p class="font-medium text-kumo-default">
          Active: {surface === "account" ? "Account" : "Zone"} surface
        </p>
        <p>Click the header button to slide between views</p>
      </div>
    </DemoMain>
  </Sidebar.Provider>
</DemoShell>

Full Example

Kitchen sink: account switcher, sliding views, badges, nested collapsible sub-menus, and a footer action.

Build
Protect & Connect
example.com
Main content area
<script lang="ts">
  import ArrowLeftIcon from "phosphor-svelte/lib/ArrowLeftIcon";
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import CodeIcon from "phosphor-svelte/lib/CodeIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import GlobeIcon from "phosphor-svelte/lib/GlobeIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import LockIcon from "phosphor-svelte/lib/LockIcon";
  import MagnifyingGlassIcon from "phosphor-svelte/lib/MagnifyingGlassIcon";
  import ShieldCheckIcon from "phosphor-svelte/lib/ShieldCheckIcon";
  import type { Component } from "svelte";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import AccountSwitcher from "./sidebar-account-switcher.svelte";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    badge?: string;
    class?: string;
    icon: Component;
    label: string;
    onSelect?: () => void;
    tooltip?: string;
  }

  interface SubMenuItem {
    badge?: string;
    label: string;
  }

  interface NestedSubMenuItem extends SubMenuItem {
    children: SubMenuItem[];
  }

  interface CollapsibleMenuItem extends MenuItem {
    children: Array<NestedSubMenuItem | SubMenuItem>;
  }

  interface MenuSection {
    items: Array<CollapsibleMenuItem | MenuItem>;
    label?: string;
  }

  let surface = $state<"account" | "domain">("account");

  function showDomainNavigation() {
    surface = "domain";
  }

  function showAccountNavigation() {
    surface = "account";
  }

  const sidebarNavigation = {
    account: {
      search: {
        label: "Quick search…",
        icon: MagnifyingGlassIcon,
        tooltip: "Search",
        class:
          "mb-3 ring ring-kumo-line transition-[margin] duration-250 group-data-[state=collapsed]/sidebar:mb-0 group-data-[state=collapsed]/sidebar:ring-transparent",
      } satisfies MenuItem,
      sections: [
        {
          items: [
            { label: "Home", icon: HouseIcon, active: true },
            { label: "Analytics & Logs", icon: ChartBarIcon },
            { label: "Domains", icon: GlobeIcon, onSelect: showDomainNavigation },
          ],
        },
        {
          label: "Build",
          items: [
            {
              label: "Compute",
              icon: CodeIcon,
              children: [
                {
                  label: "Workers & Pages",
                  children: [{ label: "Overview" }, { label: "Workers" }, { label: "Pages" }],
                },
                { label: "Durable Objects" },
                { label: "Containers", badge: "Beta" },
              ],
            },
            { label: "Storage", icon: DatabaseIcon },
          ],
        },
        {
          label: "Protect & Connect",
          items: [
            { label: "Security", icon: ShieldCheckIcon },
            { label: "Zero Trust", icon: LockIcon, badge: "Beta" },
          ],
        },
      ] satisfies MenuSection[],
    },
    domain: {
      sections: [
        {
          items: [{ label: "Back", icon: ArrowLeftIcon, onSelect: showAccountNavigation }],
        },
        {
          label: "example.com",
          items: [
            { label: "Overview", icon: GlobeIcon, active: true },
            { label: "Security", icon: ShieldCheckIcon },
            { label: "SSL/TLS", icon: LockIcon },
            { label: "Analytics", icon: ChartBarIcon },
            { label: "Caching", icon: DatabaseIcon },
          ],
        },
      ] satisfies MenuSection[],
    },
  };

  function hasChildren(item: CollapsibleMenuItem | MenuItem): item is CollapsibleMenuItem {
    return "children" in item;
  }

  function hasNestedChildren(item: NestedSubMenuItem | SubMenuItem): item is NestedSubMenuItem {
    return "children" in item;
  }
</script>

{#snippet menuButton(item: MenuItem)}
  {@const Icon = item.icon}
  <Sidebar.MenuButton active={item.active} onclick={item.onSelect} tooltip={item.tooltip} class={item.class}>
    {#snippet icon()}<Icon />{/snippet}
    {item.label}
    {#if item.badge}<Sidebar.MenuBadge>{item.badge}</Sidebar.MenuBadge>{/if}
  </Sidebar.MenuButton>
{/snippet}

{#snippet subMenuButton(item: SubMenuItem)}
  <Sidebar.MenuSubButton>
    {item.label}
    {#if item.badge}<Sidebar.MenuBadge>{item.badge}</Sidebar.MenuBadge>{/if}
  </Sidebar.MenuSubButton>
{/snippet}

{#snippet nestedSubMenu(item: NestedSubMenuItem)}
  <Sidebar.MenuSubItem>
    <Sidebar.Collapsible>
      <Sidebar.CollapsibleTrigger>
        {#snippet child({ props })}
          <Sidebar.MenuSubButton {...props}>
            {item.label} <Sidebar.MenuChevron />
          </Sidebar.MenuSubButton>
        {/snippet}
      </Sidebar.CollapsibleTrigger>
      <Sidebar.CollapsibleContent>
        <Sidebar.MenuSub>
          {#each item.children as child (child.label)}
            {@render subMenuButton(child)}
          {/each}
        </Sidebar.MenuSub>
      </Sidebar.CollapsibleContent>
    </Sidebar.Collapsible>
  </Sidebar.MenuSubItem>
{/snippet}

{#snippet collapsibleMenuButton(item: CollapsibleMenuItem)}
  <Sidebar.MenuItem>
    <Sidebar.Collapsible open>
      <Sidebar.CollapsibleTrigger>
        {#snippet child({ props })}
          {@const Icon = item.icon}
          <Sidebar.MenuButton {...props}>
            {#snippet icon()}<Icon />{/snippet}
            {item.label} <Sidebar.MenuChevron />
          </Sidebar.MenuButton>
        {/snippet}
      </Sidebar.CollapsibleTrigger>
      <Sidebar.CollapsibleContent>
        <Sidebar.MenuSub>
          {#each item.children as child (child.label)}
            {#if hasNestedChildren(child)}
              {@render nestedSubMenu(child)}
            {:else}
              {@render subMenuButton(child)}
            {/if}
          {/each}
        </Sidebar.MenuSub>
      </Sidebar.CollapsibleContent>
    </Sidebar.Collapsible>
  </Sidebar.MenuItem>
{/snippet}

{#snippet menuSection(section: MenuSection)}
  <Sidebar.Group>
    {#if section.label}
      <Sidebar.GroupLabel>{section.label}</Sidebar.GroupLabel>
    {/if}
    <Sidebar.Menu>
      {#each section.items as item (item.label)}
        {#if hasChildren(item)}
          {@render collapsibleMenuButton(item)}
        {:else}
          {@render menuButton(item)}
        {/if}
      {/each}
    </Sidebar.Menu>
  </Sidebar.Group>
{/snippet}

<DemoShell>
  <Sidebar.Provider contained defaultOpen peekable class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Header>
        <AccountSwitcher />
      </Sidebar.Header>

      <Sidebar.SlidingViews activeKey={surface} direction={surface === "domain" ? "left" : "right"}>
        <Sidebar.SlidingView value="account">
          <Sidebar.Content>
            <Sidebar.Group>
              <Sidebar.Menu>
                {@render menuButton(sidebarNavigation.account.search)}
              </Sidebar.Menu>
            </Sidebar.Group>

            {#each sidebarNavigation.account.sections as section (section.label ?? section.items[0]?.label)}
              {@render menuSection(section)}
            {/each}
          </Sidebar.Content>
        </Sidebar.SlidingView>

        <Sidebar.SlidingView value="domain">
          <Sidebar.Content>
            {#each sidebarNavigation.domain.sections as section (section.label ?? section.items[0]?.label)}
              {@render menuSection(section)}
            {/each}
          </Sidebar.Content>
        </Sidebar.SlidingView>
      </Sidebar.SlidingViews>

      <Sidebar.Footer>
        <Sidebar.Trigger />
      </Sidebar.Footer>
    </Sidebar.Root>
    <DemoMain />
  </Sidebar.Provider>
</DemoShell>

Mobile

Use mobileBreakpoint to choose when the sidebar switches to a mobile drawer. This demo sets a high breakpoint to force the mobile variant.

Company
Overview
Build

Click the button to open the mobile sidebar

Press Escape or click the backdrop to close

<script lang="ts">
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import CodeIcon from "phosphor-svelte/lib/CodeIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import GlobeIcon from "phosphor-svelte/lib/GlobeIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import type { Component } from "svelte";
  import { useSidebar } from "kumo-svelte/components/sidebar";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import BrandLogo from "./sidebar-brand-logo.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    icon: Component;
    label: string;
  }

  interface Section {
    items: MenuItem[];
    label: string;
  }

  const sections: Section[] = [
    {
      label: "Overview",
      items: [
        { label: "Home", icon: HouseIcon, active: true },
        { label: "Analytics", icon: ChartBarIcon },
        { label: "Domains", icon: GlobeIcon },
      ],
    },
    {
      label: "Build",
      items: [
        { label: "Compute", icon: CodeIcon },
        { label: "Storage", icon: DatabaseIcon },
      ],
    },
  ];
</script>

{#snippet mobileToggleButton()}
  {@const sidebar = useSidebar("MobileToggleButton")}
  <button
    type="button"
    onclick={sidebar.toggleSidebar}
    class="cursor-pointer rounded-lg border border-kumo-line bg-kumo-base px-3 py-1.5 text-base text-kumo-default transition-colors hover:bg-kumo-tint"
  >
    {sidebar.openMobile ? "Close sidebar" : "Open sidebar"}
  </button>
{/snippet}

<div class="relative h-[540px] w-full overflow-hidden rounded-lg border border-kumo-line bg-kumo-base">
  <Sidebar.Provider contained mobileBreakpoint={9999} class="h-full">
    <Sidebar.Root>
      <Sidebar.Header>
        <BrandLogo />
      </Sidebar.Header>
      <Sidebar.Content>
        {#each sections as section (section.label)}
          <Sidebar.Group>
            <Sidebar.GroupLabel>{section.label}</Sidebar.GroupLabel>
            <Sidebar.Menu>
              {#each section.items as item (item.label)}
                {@const Icon = item.icon}
                <Sidebar.MenuButton active={item.active}>
                  {#snippet icon()}<Icon />{/snippet}
                  {item.label}
                </Sidebar.MenuButton>
              {/each}
            </Sidebar.Menu>
          </Sidebar.Group>
        {/each}
      </Sidebar.Content>
      <Sidebar.Footer>
        <Sidebar.Trigger />
      </Sidebar.Footer>
    </Sidebar.Root>
    <DemoMain>
      {@render mobileToggleButton()}
      <p>Click the button to open the mobile sidebar</p>
      <p class="text-sm text-kumo-subtle">Press Escape or click the backdrop to close</p>
    </DemoMain>
  </Sidebar.Provider>
</div>

Collapsible Groups

Add collapsible to a Sidebar.Group and wrap the menu in Sidebar.GroupContent to enable animated expand/collapse via the group label. This is a Svelte-specific convenience on top of the upstream Sidebar primitives.

Overview
Main content area
<script lang="ts">
  import ChartBarIcon from "phosphor-svelte/lib/ChartBarIcon";
  import CodeIcon from "phosphor-svelte/lib/CodeIcon";
  import DatabaseIcon from "phosphor-svelte/lib/DatabaseIcon";
  import GlobeIcon from "phosphor-svelte/lib/GlobeIcon";
  import HouseIcon from "phosphor-svelte/lib/HouseIcon";
  import LockIcon from "phosphor-svelte/lib/LockIcon";
  import ShieldCheckIcon from "phosphor-svelte/lib/ShieldCheckIcon";
  import type { Component } from "svelte";
  import * as Sidebar from "kumo-svelte/components/sidebar";
  import DemoShell from "./sidebar-demo-shell.svelte";
  import DemoMain from "./sidebar-main.svelte";

  interface MenuItem {
    active?: boolean;
    icon: Component;
    label: string;
  }

  const overviewItems: MenuItem[] = [
    { label: "Home", icon: HouseIcon, active: true },
    { label: "Analytics", icon: ChartBarIcon },
    { label: "Domains", icon: GlobeIcon },
  ];

  const buildItems: MenuItem[] = [
    { label: "Compute", icon: CodeIcon },
    { label: "Storage", icon: DatabaseIcon },
  ];

  const protectItems: MenuItem[] = [
    { label: "Security", icon: ShieldCheckIcon },
    { label: "Zero Trust", icon: LockIcon },
  ];
</script>

<DemoShell>
  <Sidebar.Provider contained defaultOpen class="h-full min-h-0!">
    <Sidebar.Root>
      <Sidebar.Content>
        <Sidebar.Group>
          <Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
          <Sidebar.Menu>
            {#each overviewItems as item (item.label)}
              {@const Icon = item.icon}
              <Sidebar.MenuButton active={item.active}>
                {#snippet icon()}<Icon />{/snippet}
                {item.label}
              </Sidebar.MenuButton>
            {/each}
          </Sidebar.Menu>
        </Sidebar.Group>

        <Sidebar.Group collapsible defaultOpen>
          <Sidebar.GroupLabel>Build</Sidebar.GroupLabel>
          <Sidebar.GroupContent>
            <Sidebar.Menu>
              {#each buildItems as item (item.label)}
                {@const Icon = item.icon}
                <Sidebar.MenuButton>
                  {#snippet icon()}<Icon />{/snippet}
                  {item.label}
                </Sidebar.MenuButton>
              {/each}
            </Sidebar.Menu>
          </Sidebar.GroupContent>
        </Sidebar.Group>

        <Sidebar.Group collapsible defaultOpen={false}>
          <Sidebar.GroupLabel>Protect & Connect</Sidebar.GroupLabel>
          <Sidebar.GroupContent>
            <Sidebar.Menu>
              {#each protectItems as item (item.label)}
                {@const Icon = item.icon}
                <Sidebar.MenuButton>
                  {#snippet icon()}<Icon />{/snippet}
                  {item.label}
                </Sidebar.MenuButton>
              {/each}
            </Sidebar.Menu>
          </Sidebar.GroupContent>
        </Sidebar.Group>
      </Sidebar.Content>
    </Sidebar.Root>
    <DemoMain />
  </Sidebar.Provider>
</DemoShell>

API Reference

Sidebar

PropTypeDefault
children*Snippet—
classstring—
contentClassNamestring—
refHTMLElement | nullnull

Sidebar.Collapsible

PropTypeDefault
autoScrollOnOpenbooleanfalse
children*Snippet—
classstring—
defaultOpenbooleanfalse
disabledbooleanfalse
idstring—
openbooleandefaultOpen
onOpenChange(open: boolean) => void—
onOpenChangeComplete(open: boolean) => void—

Sidebar.CollapsibleContent

PropTypeDefault
classstring—

Sidebar.CollapsibleTrigger

PropTypeDefault
classstring—

Sidebar.Content

No component-specific props. This component accepts child content or standard forwarded attributes.

Sidebar.Footer

No component-specific props. This component accepts child content or standard forwarded attributes.

Sidebar.Group

PropTypeDefault
childrenSnippet—
classstring—
collapsiblebooleanfalse
defaultOpenbooleantrue
onOpenChange(open: boolean) => void—
openbooleandefaultOpen

Sidebar.GroupContent

PropTypeDefault
childrenSnippet—
classstring—

Sidebar.GroupLabel

PropTypeDefault
childrenSnippet—
classstring—

Sidebar.Header

No component-specific props. This component accepts child content or standard forwarded attributes.

Sidebar.Input

PropTypeDefault
childrenSnippet—
classstring—
placeholderstring"Search..."
shortcutstring—

Sidebar.Menu

PropTypeDefault
childrenSnippet—
classstring—

Sidebar.MenuAction

PropTypeDefault
childrenSnippet—
classstring—

Sidebar.MenuBadge

PropTypeDefault
childrenSnippet—
classstring—

Sidebar.MenuButton

PropTypeDefaultDescription
activebooleanfalse—
childSnippet<[{ props: ChildProps }]>—Render a custom interactive element. Spread `props` onto that element.
childrenSnippet——
classstring——
hrefstring——
iconSnippet——
linkPropsOmit<HTMLAnchorAttributes, "children" | "class" | "href">——
sizeSidebarMenuButtonSize"base"—
tooltipstring——

Sidebar.MenuChevron

PropTypeDefault
classstring—

Sidebar.MenuItem

PropTypeDefault
childrenSnippet—
classstring—

Sidebar.MenuSub

PropTypeDefault
childrenSnippet—
classstring—

Sidebar.MenuSubButton

PropTypeDefaultDescription
activebooleanfalse—
childSnippet<[{ props: ChildProps }]>—Render a custom interactive element. Spread `props` onto that element.
childrenSnippet——
classstring——
hrefstring——
linkPropsOmit<HTMLAnchorAttributes, "children" | "class" | "href">——

Sidebar.MenuSubItem

PropTypeDefault
childrenSnippet—
classstring—

Sidebar.Provider

PropTypeDefault
animationDurationnumberKUMO_SIDEBAR_STYLING.animation.duration
children*Snippet—
classstring—
collapsibleSidebarCollapsibleKUMO_SIDEBAR_DEFAULT_VARIANTS.collapsible
containedbooleanfalse
defaultOpenbooleantrue
defaultWidthnumberDEFAULT_WIDTH_PX
maxWidthnumberMAX_WIDTH_PX
minWidthnumberMIN_WIDTH_PX
mobileBreakpointnumberKUMO_SIDEBAR_STYLING.mobile.breakpoint
onOpenChange(open: boolean) => void—
onWidthChange(width: number) => void—
openbooleandefaultOpen
peekablebooleanfalse
resizablebooleanfalse
sideSidebarSideKUMO_SIDEBAR_DEFAULT_VARIANTS.side
stylestring—
variantSidebarVariantKUMO_SIDEBAR_DEFAULT_VARIANTS.variant
widthnumberdefaultWidth

Sidebar.Rail

PropTypeDefault
classstring—

Sidebar.ResizeHandle

PropTypeDefault
classstring—

Sidebar.Separator

PropTypeDefault
classstring—

Sidebar.SlidingView

PropTypeDefaultDescription
childrenSnippet——
classstring——
refHTMLDivElement | nullnull—
value*string—Unique key matching this view. Must correspond to `activeKey` on `SidebarSlidingViews`.

Sidebar.SlidingViews

PropTypeDefaultDescription
activeKey*string—Key of the currently active view. Must match a child `SidebarSlidingView` value.
children*Snippet——
classstring——
direction"left" | "right""left"Slide direction for the transition. - `left`: new view slides in from the right - `right`: new view slides in from the left
refHTMLDivElement | nullnull—

Sidebar.Trigger

PropTypeDefault
childrenSnippet—
classstring—

Icon Snippets

For menu button icons, place a named icon snippet inside Sidebar.MenuButton so the icon stays colocated with its label.

<Sidebar.MenuButton active>
  {#snippet icon()}<HouseIcon />{/snippet}
  Home
</Sidebar.MenuButton>

Child Snippets

Sidebar.MenuButton and Sidebar.MenuSubButton render a button or link by default. Use a child snippet when you need to render a custom interactive element, and forward the provided props to that element. This follows the Bits UI child snippet pattern.

<Sidebar.MenuButton>
  {#snippet child({ props })}
    <a href="/home" {...props}>
      <HouseIcon />
      <span>Home</span>
    </a>
  {/snippet}
</Sidebar.MenuButton>

On this page

  • Import
  • Usage
  • Examples
    • Basic
    • Toggle & Collapsed State
    • Resizable
    • Right Side
    • Peeking
    • Auto Scroll
    • Sliding Views
    • Full Example
    • Mobile
    • Collapsible Groups
  • API Reference
    • Sidebar
    • Sidebar.Collapsible
    • Sidebar.CollapsibleContent
    • Sidebar.CollapsibleTrigger
    • Sidebar.Content
    • Sidebar.Footer
    • Sidebar.Group
    • Sidebar.GroupContent
    • Sidebar.GroupLabel
    • Sidebar.Header
    • Sidebar.Input
    • Sidebar.Menu
    • Sidebar.MenuAction
    • Sidebar.MenuBadge
    • Sidebar.MenuButton
    • Sidebar.MenuChevron
    • Sidebar.MenuItem
    • Sidebar.MenuSub
    • Sidebar.MenuSubButton
    • Sidebar.MenuSubItem
    • Sidebar.Provider
    • Sidebar.Rail
    • Sidebar.ResizeHandle
    • Sidebar.Separator
    • Sidebar.SlidingView
    • Sidebar.SlidingViews
    • Sidebar.Trigger
  • Icon Snippets
  • Child Snippets