Sidebar
A persistent layout nav panel, distinct from Drawer (which overlays content and dismisses). A Sidebar never overlays: it collapses IN PLACE to a narrow icon rail.
Real use · app shell (collapsible, cross-tree trigger)
dashboard
The trigger above lives OUTSIDE the sidebar (in this main content area) yet still toggles it — both triggers share one SidebarProvider.
Using Sidebar
Silica ships one design system through three paths. They share a class vocabulary but are not interchangeable — pick the one that matches what you are building.
Plain HTML — CSS classes
The Tailwind v4 plugin. Works in any framework or none — Rails, Django, PHP, Go, a static file. This path ships no JavaScript, so Sidebar is styled but inert: its behavior comes from sidebar. For that behavior with no framework, use the node-tree path below.
/* in your CSS */
@import "tailwindcss";
@plugin "@wizeworks/silicaui";Root class sidebar, with 19 modifiers:
.sidebar.sidebar-accent.sidebar-content.sidebar-error.sidebar-footer.sidebar-group.sidebar-group-label.sidebar-header.sidebar-header-brand.sidebar-info.sidebar-item.sidebar-item-icon.sidebar-item-label.sidebar-item-trailing.sidebar-neutral.sidebar-primary.sidebar-secondary.sidebar-success.sidebar-trigger.sidebar-warning
React
Typed components over those same classes, with behavior and accessibility from Base UI.
npm i @wizeworks/silicaui-reactimport { Sidebar } from "@wizeworks/silicaui-react";Props
SidebarProviderProps
| Prop | Type | Required | Description |
|---|---|---|---|
| children | React.ReactNode | yes | — |
| collapsed | boolean | no | Controlled collapsed state. |
| defaultCollapsed | boolean | no | Uncontrolled initial collapsed state. Default false. |
| onCollapsedChange | (collapsed: boolean) => void | no | — |
SidebarProps
| Prop | Type | Required | Description |
|---|---|---|---|
| side | SidebarSide | no | Which edge the panel sits on. Default left. |
| color | SilicaColor | no | Accent color for the active SidebarItem; maps to sidebar-<color>. |
| collapsed | boolean | no | Explicit collapsed override. When omitted, reads the ancestor SidebarProvider (defaults to expanded if neither is present). |
SidebarItemProps
| Prop | Type | Required | Description |
|---|---|---|---|
| icon | React.ReactNode | no | Leading icon. |
| trailing | React.ReactNode | no | Trailing content (badge, chevron); hidden when collapsed. |
| active | boolean | no | Highlights the row with the sidebar's accent color. |
| disabled | boolean | no | — |
| as | React.ElementType | no | Render as a different element, typically "a" (or a router's Link) for real navigation — AllHTMLAttributes (not the plain HTMLAttributes most components here use) is deliberate so href/target/etc. |
| render | React.ReactElement | no | Render as a different element, composing your props with this component's — the same render Button/Badge/Card take, and Base UI's own composition model: <SidebarItem render={<Link href="/shipments" />}>…</SidebarItem> Preferred over as, because the element carries its OWN props and they are type-checked against it (SidebarItem cannot know that Link needs an href). |
Source of the demo above
The exact file that renders the examples above, so it can never drift from them. It imports a few of this site’s own layout helpers (../lib/…), so read it for the component calls rather than copying it whole.
Show source
import { useState } from "react";
import {
SidebarProvider,
Sidebar,
SidebarHeader,
SidebarHeaderBrand,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupLabel,
SidebarItem,
SidebarTrigger,
Wordmark,
Avatar,
} from "@wizeworks/silicaui-react";
import { Section } from "../lib/Section";
const HomeIcon = (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 12 12 3l9 9" /> <path d="M5 10v10h14V10" />
</svg>
);
const SearchIcon = (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8" /> <path d="m21 21-4.3-4.3" />
</svg>
);
const UsersIcon = (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /> <circle cx="9" cy="7" r="4" />
<path d="M22 21v-2a4 4 0 0 0-3-3.87" /> <path d="M16 3.13a4 4 0 0 1 0 7.75" />
</svg>
);
const SettingsIcon = (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 17H5" /> <path d="M19 7h-9" /> <circle cx="17" cy="17" r="3" /> <circle cx="7" cy="7" r="3" />
</svg>
);
function AppShell() {
const [active, setActive] = useState("dashboard");
return (
<div className="flex h-[26rem] w-full max-w-2xl overflow-hidden rounded-box border border-base-200">
<Sidebar>
<SidebarHeader>
<SidebarHeaderBrand>
<Wordmark size="sm" color="primary">Acme</Wordmark>
</SidebarHeaderBrand>
<SidebarTrigger />
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Workspace</SidebarGroupLabel>
<SidebarItem
icon={HomeIcon}
active={active === "dashboard"}
onClick={() => setActive("dashboard")}
>
Dashboard
</SidebarItem>
<SidebarItem
icon={SearchIcon}
active={active === "search"}
onClick={() => setActive("search")}
>
Search
</SidebarItem>
<SidebarItem
icon={UsersIcon}
active={active === "team"}
onClick={() => setActive("team")}
trailing={<span className="badge badge-primary badge-xs">4</span>}
>
Team
</SidebarItem>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>Account</SidebarGroupLabel>
<SidebarItem
icon={SettingsIcon}
active={active === "settings"}
onClick={() => setActive("settings")}
>
Settings
</SidebarItem>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<SidebarItem icon={<Avatar size="xs">AL</Avatar>}>Ada Lovelace</SidebarItem>
</SidebarFooter>
</Sidebar>
<div className="flex flex-1 flex-col gap-3 overflow-y-auto p-6">
<div className="flex items-center gap-2">
<SidebarTrigger />
<h4 className="text-sm font-semibold capitalize">{active}</h4>
</div>
<p className="text-sm opacity-70">
The trigger above lives OUTSIDE the sidebar (in this main content
area) yet still toggles it — both triggers share one
SidebarProvider.
</p>
</div>
</div>
);
}
export function SidebarDemo() {
return (
<Section title="Real use · app shell (collapsible, cross-tree trigger)">
<SidebarProvider>
<AppShell />
</SidebarProvider>
</Section>
);
}Vanilla — node tree to HTML
Sidebar — a persistent nav panel (<aside>) that collapses in place to an icon rail, unlike Drawer (which overlays and dismisses). Build a node tree, project it with toHtml(), and let the zero-dependency runtime hydrate it. This is the path for generated or user-authored documents — a site builder, CMS output, a static export.
npm i @wizeworks/silicaui-html @wizeworks/silicaui-behaviorsimport { atom, toHtml } from "@wizeworks/silicaui-html";
const node = atom("Sidebar", "sidebar", { /* props */ }, [/* children */]);
const markup = toHtml(node);Styling on this path is the class string in the second argument — the same classes as the Plain HTML section above. There is no color prop here; write "sidebar sidebar-primary" rather than passing a colour as a prop.
- Takes children
- yes
- Hydrated by
sidebar
Load @wizeworks/silicaui-behaviors on the page or this markup renders correctly and does nothing.
The full API for Sidebar, on every path, is available through the Silica MCP server.