Skip to content
WANCRAFT

D.77Navigation & Menus

Collapsible Sidebar

An app sidebar that springs down to an icon rail, with fading labels, rail tooltips, nested groups and a sliding active marker.

NewReactMotionTailwindintermediate

Overview

Collapsible Sidebar is an application navigation column that collapses to an icon rail on a spring width animation. Labels fade and slide out rather than reflowing, badges shrink to a dot on the icon, and while collapsed every item shows its label as a tooltip on hover or keyboard focus. Items with children become disclosure groups whose lists open with a height animation; in the rail, activating a group expands the sidebar and opens it. A shared-layout indicator slides to the active item, falling back to its parent group when the child is hidden. The toggle is a button with aria-expanded and an optional ⌘ / Ctrl shortcut. State is controlled (collapsed + onCollapsedChange) or uncontrolled, and persistence is left to you.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/collapsible-sidebar.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/collapsible-sidebar.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/collapsible-sidebar.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { CollapsibleSidebar, type SidebarItem } from "@/components/ui/collapsible-sidebar";
import { Folder, Home, Inbox, Settings } from "lucide-react";

export function CollapsibleSidebarExample() {
  const items: SidebarItem[] = [
    { id: "home", label: "Home", icon: <Home />, href: "/" },
    { id: "inbox", label: "Inbox", icon: <Inbox />, href: "/inbox", badge: 3 },
    {
      id: "projects",
      label: "Projects",
      icon: <Folder />,
      children: [
        { id: "atlas", label: "Atlas", href: "/projects/atlas" },
        { id: "beacon", label: "Beacon", href: "/projects/beacon" },
      ],
    },
    { id: "settings", label: "Settings", icon: <Settings />, href: "/settings" },
  ];

  return (
    <div className="flex h-screen">
      <CollapsibleSidebar
        items={items}
        defaultActiveId="home"
        header={<span>Workspace</span>}
      />
      <main className="flex-1">…</main>
    </div>
  );
}

03Props

PropTypeDefaultDescription
itemsSidebarItem[]—{ id, label, icon?, href?, badge?, children? }. Children form a disclosure group.
activeIdstring—Active item id (controlled).
defaultActiveIdstring—Initially active item id (uncontrolled).
onSelect(item, event) => void—Called when an item is activated; call event.preventDefault() for client routing.
collapsedboolean—Collapsed state (controlled).
defaultCollapsedbooleanfalseInitial collapsed state (uncontrolled).
onCollapsedChange(collapsed: boolean) => void—Called when the sidebar asks to collapse or expand; persist it here.
expandedWidthnumber248Expanded width, in px.
collapsedWidthnumber64Icon rail width, in px.
showTooltipsbooleantrueShow labels as tooltips on hover / focus while collapsed.
shortcutstring""Key that toggles with ⌘ / Ctrl (page-wide listener). Empty string disables it.
headerReactNode—Brand or workspace content beside the toggle.
footerReactNode—Content pinned to the bottom.
labelstring"Sidebar"Accessible name of the nav landmark.
toggleLabelstring"Toggle sidebar"Accessible name of the toggle button.
spring{ stiffness: number; damping: number; mass: number }{ stiffness: 380, damping: 36, mass: 0.9 }Spring for the width and the active indicator.
accentstring"#FF7448"Active marker, badge dot and focus ring colour.
...propsHTMLAttributes<HTMLElement>—Native attributes are forwarded to the <aside>.

04Features

  • 01Spring width animation between the expanded panel and an icon rail
  • 02Labels fade and slide instead of reflowing; badges shrink to a dot on the icon
  • 03Rail tooltips on hover and keyboard focus
  • 04Nested disclosure groups with height animation; groups open the sidebar from the rail
  • 05Shared-layout active indicator that falls back to the parent of a hidden child
  • 06Controlled or uncontrolled collapse, with an optional ⌘ / Ctrl shortcut
motion · springmotion · layoutmotion · tweeninput · clickinput · hoverinput · focusinput · keyboard

05Accessibility

  • The toggle is a labelled button with aria-expanded and aria-controls pointing at the nav; aria-keyshortcuts lists the shortcut when set.
  • Items are real links (or buttons) in a labelled <nav>; the active one carries aria-current="page".
  • Labels stay in the accessible name while collapsed, so the visual-only tooltips are aria-hidden.
  • Groups are disclosure buttons with aria-expanded and aria-controls.
  • Tooltips appear on keyboard focus as well as hover; on touch devices the hover state is not used.
  • With prefers-reduced-motion the width, labels and indicator change instantly.

06Limitations

  • Nested items are only reachable when expanded; activating a group in the rail expands the sidebar first.
  • The shortcut listens on the whole window; avoid keys the host app already uses.