For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. An agent skill is available at /.well-known/agent-skills/site-skill.md.
Home

Sidebar

A collapsible app sidebar with navigation groups, search, badges, tooltips, and mobile sheet support.

Dashboard
Main content area
import { HomeIcon, InboxIcon, LayoutDashboardIcon, SearchIcon, SettingsIcon, UsersIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarSeparator, SidebarTrigger } from "@/components/ui/sidebar";

const navItems = [
  { active: false, icon: HomeIcon, label: "Home" },
  { active: true, icon: LayoutDashboardIcon, label: "Dashboard" },
  { active: false, badge: "4", icon: InboxIcon, label: "Inbox" },
  { active: false, icon: UsersIcon, label: "Team" },
  { active: false, icon: SettingsIcon, label: "Settings" },
];

export const SidebarDemo = () => (
    <SidebarProvider className="h-[480px] min-h-0 overflow-hidden rounded-lg border contain-strict">
      <Sidebar collapsible="icon">
        <SidebarHeader>
          <SidebarMenu>
            <SidebarMenuItem>
              <SidebarMenuButton size="lg" tooltip="GIN Design">
                <div className="bg-primary text-primary-foreground flex size-8 shrink-0 items-center justify-center rounded-md text-xs font-bold">GIN</div>
                <span className="font-semibold">GIN Design</span>
              </SidebarMenuButton>
            </SidebarMenuItem>
          </SidebarMenu>
        </SidebarHeader>
        <SidebarContent>
          <SidebarGroup><SidebarGroupContent><SidebarInput placeholder="Search…" /></SidebarGroupContent></SidebarGroup>
          <SidebarSeparator />
          <SidebarGroup>
            <SidebarGroupLabel>Navigation</SidebarGroupLabel>
            <SidebarGroupContent>
              <SidebarMenu>
                {navItems.map((item) => (
                  <SidebarMenuItem key={item.label}>
                    <SidebarMenuButton isActive={item.active} tooltip={item.label}>
                      <item.icon />
                      <span>{item.label}</span>
                    </SidebarMenuButton>
                    {item.badge && <SidebarMenuBadge>{item.badge}</SidebarMenuBadge>}
                  </SidebarMenuItem>
                ))}
              </SidebarMenu>
            </SidebarGroupContent>
          </SidebarGroup>
        </SidebarContent>
        <SidebarFooter>
          <SidebarMenu>
            <SidebarMenuItem>
              <SidebarMenuButton tooltip="Account" size="lg">
                <div className="bg-muted size-8 shrink-0 rounded-full" />
                <div className="flex flex-col text-left text-xs leading-tight">
                  <span className="font-medium">Nuno Santos</span>
                  <span className="text-muted-foreground truncate">nunosantos@nunosantos.pt</span>
                </div>
              </SidebarMenuButton>
            </SidebarMenuItem>
          </SidebarMenu>
        </SidebarFooter>
      </Sidebar>
      <SidebarInset>
        <header className="flex h-12 items-center gap-2 border-b px-4">
          <SidebarTrigger />
          <span className="text-sm font-medium">Dashboard</span>
        </header>
        <div className="flex flex-1 items-center justify-center p-8">
          <div className="text-muted-foreground flex flex-col items-center gap-2 text-center text-sm">
            <LayoutDashboardIcon className="size-8 opacity-30" />
            <span>Main content area</span>
            <Button variant="outline" size="sm">
              <SearchIcon />
              Browse components
            </Button>
          </div>
        </div>
      </SidebarInset>
    </SidebarProvider>
  );

Layout

Wrap your page with SidebarProvider and place Sidebar and SidebarInset as siblings. SidebarProvider manages open state, keyboard shortcut (⌘B), and cookie persistence.

<SidebarProvider>
  <Sidebar>...</Sidebar>
  <SidebarInset>
    <header>
      <SidebarTrigger />
    </header>
    {/* page content */}
  </SidebarInset>
</SidebarProvider>

Collapsible

Set collapsible on Sidebar to control collapse behaviour.

<Sidebar collapsible="offcanvas"> {/* slides fully off-screen (default) */}
<Sidebar collapsible="icon">     {/* collapses to icon-only width */}
<Sidebar collapsible="none">     {/* always visible, never collapses */}

Variant

<Sidebar variant="sidebar">   {/* default — full bleed to viewport edge */}
<Sidebar variant="floating">  {/* floating card with rounded corners + shadow */}
<Sidebar variant="inset">     {/* main content has rounded-xl inset */}

Side

<Sidebar side="left">   {/* default */}
<Sidebar side="right">
<SidebarContent>
  <SidebarGroup>
    <SidebarGroupLabel>Navigation</SidebarGroupLabel>
    <SidebarGroupContent>
      <SidebarMenu>
        <SidebarMenuItem>
          <SidebarMenuButton isActive tooltip="Dashboard">
            <LayoutDashboardIcon />
            <span>Dashboard</span>
          </SidebarMenuButton>
          <SidebarMenuBadge>4</SidebarMenuBadge>
        </SidebarMenuItem>
      </SidebarMenu>
    </SidebarGroupContent>
  </SidebarGroup>
</SidebarContent>

Tooltips on collapsed icon mode

Pass a tooltip string (or TooltipContent props) to SidebarMenuButton. Tooltips auto-show only when the sidebar is collapsed to icon mode.

<SidebarMenuButton tooltip="Settings">
  <SettingsIcon />
  <span>Settings</span>
</SidebarMenuButton>
<SidebarMenuItem>
  <SidebarMenuButton>
    <FolderIcon />
    <span>Projects</span>
  </SidebarMenuButton>
  <SidebarMenuSub>
    <SidebarMenuSubItem>
      <SidebarMenuSubButton isActive>Website</SidebarMenuSubButton>
    </SidebarMenuSubItem>
    <SidebarMenuSubItem>
      <SidebarMenuSubButton>Mobile app</SidebarMenuSubButton>
    </SidebarMenuSubItem>
  </SidebarMenuSub>
</SidebarMenuItem>

Use SidebarMenuAction for a secondary icon button that appears on hover.

<SidebarMenuItem>
  <SidebarMenuButton>
    <FolderIcon /><span>Projects</span>
  </SidebarMenuButton>
  <SidebarMenuAction showOnHover>
    <PlusIcon />
  </SidebarMenuAction>
</SidebarMenuItem>

Loading skeleton

Use SidebarMenuSkeleton while content is loading.

{isLoading ? (
  Array.from({ length: 5 }).map((_, i) => (
    <SidebarMenuItem key={i}>
      <SidebarMenuSkeleton showIcon />
    </SidebarMenuItem>
  ))
) : (
  navItems.map(...)
)}

Controlled

const [open, setOpen] = useState(true);
 
<SidebarProvider open={open} onOpenChange={setOpen}>
  ...
</SidebarProvider>

useSidebar hook

Access sidebar state from any child component.

const { state, open, toggleSidebar, isMobile } = useSidebar();
// state: "expanded" | "collapsed"

Props

SidebarProvider

PropTypeDefaultDescription
defaultOpenbooleantrueUncontrolled initial state
openboolean—Controlled open state
onOpenChange(open: boolean) => void—Callback on state change
PropTypeDefaultDescription
side"left" | "right""left"Which side the sidebar is on
variant"sidebar" | "floating" | "inset""sidebar"Visual variant
collapsible"offcanvas" | "icon" | "none""offcanvas"Collapse behaviour

SidebarMenuButton

PropTypeDefaultDescription
isActivebooleanfalseHighlights as the active route
variant"default" | "outline""default"Visual style
size"sm" | "default" | "lg""default"Button height
tooltipstring | TooltipContentProps—Tooltip shown in icon-collapsed mode
asChildbooleanfalseRender as child element (e.g. <Link>)
soundFeedbackType—Audio feedback on click

Usage

import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarHeader,
  SidebarInset,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarProvider,
  SidebarTrigger,
} from "@/components/ui/sidebar";

Specs

# Mint — Sidebar Specs
 
## Component
- name: sidebar
- file: registry/mint/sidebar.tsx
- installs to: components/ui/sidebar.tsx
- dependencies: radix-ui, lucide-react, class-variance-authority
- registryDependencies: button, input, separator, sheet, skeleton, tooltip
 
## What is it
A full app sidebar system composed of 20+ sub-components orchestrated by
SidebarProvider context. Supports desktop expand/collapse (offcanvas or icon
mode), mobile Sheet overlay, keyboard shortcut (⌘B), and cookie-persisted
open state. Prefer this system for app shells with persistent navigation; do
not use for simple dropdowns or transient menus.
 
## Visual identity
Off-white sidebar surface (--sidebar) sits slightly apart from the page
background, giving it a calm, structural presence. Active items and hover
states use --sidebar-accent, a subtle tinted fill that avoids heavy contrast.
Mint's 1.25rem radius carries through every interactive element for a
consistently rounded, airy look.
 
## Design tokens
--sidebar: oklch(0.985 0 0) / oklch(0.205 0 0)                        — sidebar background surface
--sidebar-foreground: (inherits --foreground chain)                    — default text in sidebar
--sidebar-primary: oklch(0.205 0 0) / oklch(0.488 0.243 264.376)      — active item fill (blue in dark)
--sidebar-accent: (close to --accent)                                  — hover / active item background
--sidebar-accent-foreground: (close to --accent-foreground)            — text on hover / active
--sidebar-border: oklch(0.922 0 0) / oklch(1 0 0 / 10%)               — internal dividers, rail, sub-menu border-l
--sidebar-ring: oklch(0.708 0 0) / oklch(0.439 0 0)                   — focus ring on sidebar interactive elements
--background: oklch(1 0 0) / oklch(0.145 0 0)                         — SidebarInset and SidebarInput surface
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155)                           — focus ring (non-sidebar elements)
 
## Sub-components
- SidebarProvider       → context root; sets --sidebar-width CSS vars, wraps TooltipProvider, persists state to cookie
- Sidebar               → desktop fixed panel or mobile Sheet depending on viewport
- SidebarTrigger        → ghost icon Button (PanelLeftIcon) that calls toggleSidebar
- SidebarRail           → invisible hover strip on sidebar edge; click toggles sidebar
- SidebarInset          → <main> sibling to Sidebar; gains rounded-xl + shadow in "inset" variant
- SidebarInput          → Input pre-styled for sidebar (bg-background, h-8, shadow-none)
- SidebarHeader         → flex-col gap-2 p-2 slot at top of sidebar
- SidebarFooter         → flex-col gap-2 p-2 slot at bottom of sidebar
- SidebarSeparator      → Separator with bg-sidebar-border mx-2 for horizontal dividers
- SidebarContent        → scrollable flex-1 area; overflow hidden in icon-collapse mode
- SidebarGroup          → p-2 grouping wrapper with relative positioning
- SidebarGroupLabel     → text-xs label (text-sidebar-foreground/70); collapses to opacity-0 in icon mode
- SidebarGroupAction    → absolute icon button at group top-right; hidden in icon mode
- SidebarGroupContent   → w-full text-sm wrapper for group body
- SidebarMenu           → <ul> flex-col gap-1 nav list
- SidebarMenuItem       → <li> with group/menu-item context for action/badge peer selectors
- SidebarMenuButton     → primary interactive nav item; cva with variant + size; supports tooltip, sound, haptic
- SidebarMenuAction     → absolute secondary icon button (e.g. +); showOnHover fades until hover/focus on md+
- SidebarMenuBadge      → absolute numeric badge right of menu item; hidden in icon mode
- SidebarMenuSkeleton   → loading placeholder; showIcon adds a 16px skeleton icon; width is deterministic via useId
- SidebarMenuSub        → <ul> with border-l (border-sidebar-border) for nested items; hidden in icon mode
- SidebarMenuSubItem    → <li> wrapper for sub-menu entries
- SidebarMenuSubButton  → <a> or asChild sub-item; size sm|md; isActive highlights with sidebar-accent
 
## Variants
### SidebarMenuButton — variant
- default   → transparent background; hover fills with --sidebar-accent. Standard nav item.
- outline   → bg-background with 1px sidebar-border shadow ring; hover shifts ring to sidebar-accent. Use for elevated or call-out items.
 
### SidebarMenuButton — size
- sm        → h-7 text-xs. Dense navigation lists.
- default   → h-8 text-sm. Standard nav items.
- lg        → h-12 text-sm. Large items such as user profile rows; collapses to p-0 in icon mode.
 
### Sidebar — variant
- sidebar   → full-bleed to viewport edge with border-r/border-l divider.
- floating  → p-2 with rounded-lg, border, and shadow-sm inner panel.
- inset     → SidebarInset gains rounded-xl + shadow-sm; sidebar has no border.
 
### Sidebar — collapsible
- offcanvas → slides fully off-screen (translate by --sidebar-width). Default.
- icon      → collapses to --sidebar-width-icon (3rem); shows icons only.
- none      → always visible; no collapse behaviour.
 
## States
- hover:           sidebar-accent background + sidebar-accent-foreground text on menu buttons and actions
- focus-visible:   2px ring in --sidebar-ring, outline-hidden
- active (click):  sidebar-accent background + sidebar-accent-foreground text
- data-[active=true]: sidebar-accent fill + font-medium; set via isActive prop on SidebarMenuButton / SidebarMenuSubButton
- data-[state=open]: hover styles remain on open dropdown trigger
- disabled:        pointer-events-none opacity-50 (both disabled and aria-disabled)
- collapsed (icon mode): SidebarGroupLabel, SidebarGroupAction, SidebarMenuBadge, SidebarMenuSub, SidebarMenuSubButton all hidden; SidebarMenuButton forced to size-8 p-2
 
## Conventions
- Always wrap the page in SidebarProvider with Sidebar and SidebarInset as siblings
- SidebarProvider persists open state to a "sidebar_state" cookie (7-day TTL); read it server-side for SSR-consistent initial state
- ⌘B / Ctrl+B toggles the sidebar globally via a window keydown listener registered in SidebarProvider
- Pass tooltip to SidebarMenuButton whenever the sidebar may collapse to icon mode — tooltips auto-suppress when expanded
- Use asChild on SidebarMenuButton / SidebarMenuSubButton to render a Next.js <Link> or <a> without an extra DOM node
- SidebarMenuAction showOnHover: pair with SidebarMenuButton in the same SidebarMenuItem — the peer selector drives visibility
- SidebarMenuSkeleton: render inside SidebarMenuItem list items to preserve consistent list structure during loading
- Sub-components expose data-slot="[name]" for CSS targeting (e.g. data-slot="sidebar-menu-button")
- sounds / haptic: SidebarMenuButton accepts sound (FeedbackType) and haptic (boolean) — GIN Design exclusives that play audio or trigger haptic feedback on click
- Do not nest SidebarMenuSub more than one level deep — the layout and icon-collapse logic is not designed for deeper nesting
- SidebarRail is optional but recommended for collapsible sidebars — it provides a drag-target affordance without visible UI chrome
 
## CSS vars (set by SidebarProvider)
--sidebar-width: 16rem
--sidebar-width-icon: 3rem
--sidebar-width-mobile: 18rem (applied inline on the mobile Sheet)

Installation

GIN Design Plugin
$ npx shadcn add https://gin-design.vercel.app/sidebar