Sidebar
Preview: Plain
App navigation column. Items are links (aria-current marks the active one); groups collapse; whole bar can shrink to an icon rail.
Import
import { Sidebar, SidebarSection, SidebarItem, SidebarGroup } from '@marbleui/ui'Props
<Sidebar>
| Prop | Type | Default | Description |
|---|---|---|---|
| children *required | ReactNode | — | |
| className | string | — | |
| collapsed | boolean | — | Controlled collapse (icon-only rail). |
| collapsible | boolean | — | Show the built-in collapse toggle. |
| defaultCollapsed | boolean | false | |
| footer | ReactNode | — | |
| header | ReactNode | — | |
| onCollapsedChange | (collapsed: boolean) => void | — | |
| variant | 'floating' | 'plain' | 'plain' |
<SidebarSection>
| Prop | Type | Default | Description |
|---|---|---|---|
| children *required | ReactNode | — | |
| title | ReactNode | — |
<SidebarItem>
| Prop | Type | Default | Description |
|---|---|---|---|
| children *required | string | — | |
| icon *required | ReactNode | — | |
| badge | ReactNode | — | |
| isCurrent | boolean | — |
Also accepts React Aria props (103).
<SidebarGroup>
| Prop | Type | Default | Description |
|---|---|---|---|
| children *required | ReactNode | — | |
| icon *required | ReactNode | — | |
| title *required | string | — | |
| defaultExpanded | boolean | — |
Accessibility
Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Button · React Aria Disclosure · React Aria DisclosurePanel · React Aria Link. Follow the primitive's docs for the full keyboard map.
Usage notes
App navigation column with sections, collapsible groups and an icon-only rail. For a top bar use Navbar.
<Sidebar collapsible header={<Brand />}>
<SidebarSection title="Workspace">
<SidebarItem href="/" icon={<HomeIcon />} isCurrent>Home</SidebarItem>
<SidebarGroup icon={<FolderIcon />} title="Projects">
<SidebarItem href="/p/web" icon={<FolderIcon />}>Website</SidebarItem>
</SidebarGroup>
</SidebarSection>
</Sidebar>
- Mark the active item with
isCurrent(setsaria-current="page"). Itemchildrenmust be a string; it becomes thearia-labeland tooltip when collapsed. - Collapse is uncontrolled by default (
defaultCollapsed); control it withcollapsedandonCollapsedChange.collapsibleshows the toggle button. - In the collapsed rail,
SidebarGroupshows its children flat, so they need their ownicon. variantisplain(default) orfloating;headerandfooterare slots. The nav is labelled “Main”.
Example source · show· hide
import { Avatar } from '../avatar'
import { Badge } from '../badge'
import { Sidebar, SidebarGroup, SidebarItem, SidebarSection } from './sidebar'
const I = ({ d }: { d: string }) => (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
<path d={d} />
</svg>
)
const icons = {
home: 'M3 10.5 12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6h-6v6H4a1 1 0 0 1-1-1z',
inbox: 'M22 12h-6l-2 3h-4l-2-3H2M5.5 5.1 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.5-6.9A2 2 0 0 0 16.8 4H7.2a2 2 0 0 0-1.7 1.1',
issues: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20M12 8v4M12 16h.01',
folder: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.7-.9l-.8-1.2A2 2 0 0 0 7.9 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2z',
chart: 'M3 3v18h18M7 16l4-4 4 4 5-6',
settings: 'M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-2.9 1.2V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-2.9-1.2l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1A1.7 1.7 0 0 0 3 15H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.2-2.9l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1A1.7 1.7 0 0 0 9 4.6V4a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 2.9 1.2l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1A1.7 1.7 0 0 0 20.4 11H21a2 2 0 1 1 0 4z',
}
const Brand = () => (
<div className="flex items-center gap-2 font-semibold text-fg">
<span className="grid size-6 place-items-center rounded-md bg-accent text-xs text-accent-fg">A</span>
Acme
</div>
)
const Nav = (props: { variant?: 'plain' | 'floating'; defaultCollapsed?: boolean }) => (
<div className="flex h-[30rem] w-[44rem] overflow-hidden rounded-xl border border-border bg-bg">
<Sidebar
{...props}
collapsible
header={<Brand />}
footer={
<div className="flex items-center gap-2 px-1 text-sm">
<Avatar size="xs" name="Maya Chen" />
{!props.defaultCollapsed && <span className="truncate text-fg-muted">Maya Chen</span>}
</div>
}
>
<SidebarSection>
<SidebarItem href="#" icon={<I d={icons.home} />} isCurrent>Home</SidebarItem>
<SidebarItem href="#" icon={<I d={icons.inbox} />} badge={<Badge tone="accent">4</Badge>}>Inbox</SidebarItem>
<SidebarItem href="#" icon={<I d={icons.issues} />}>My issues</SidebarItem>
</SidebarSection>
<SidebarSection title="Workspace">
<SidebarGroup icon={<I d={icons.folder} />} title="Projects" defaultExpanded>
<SidebarItem href="#" icon={<span className="size-2 rounded-full bg-success" />}>Website</SidebarItem>
<SidebarItem href="#" icon={<span className="size-2 rounded-full bg-warning" />}>Mobile app</SidebarItem>
</SidebarGroup>
<SidebarItem href="#" icon={<I d={icons.chart} />}>Insights</SidebarItem>
<SidebarItem href="#" icon={<I d={icons.settings} />}>Settings</SidebarItem>
</SidebarSection>
</Sidebar>
<div className="flex-1 p-6 text-sm text-fg-muted">Page content</div>
</div>
)
export const styles = {
Plain: () => <Nav />,
Floating: () => <Nav variant="floating" />,
Rail: () => <Nav defaultCollapsed />,
}Source: packages/ui/src/sidebar/sidebar.tsx
Catalog
- Category
- navigation
- Priority
- Foundation
- Primitive
- composed
- React Aria
- NavigationTree