Sidebar Navigation
A whole app navigation rendered from a NavGroup[] — sections, rows, nested groups and the badges or shortcuts that trail each label. Twenty routes become one array instead of twenty near-identical blocks of JSX that drift apart. It paints no surface of its own: the panel colour, its border and the icon rail all belong to the Sidebar shell around it, and it never imports a router — pass renderLink to hand it your app's Link component. This is the configurable component; for whole shells you copy and edit — rail plus panel, floating card, plan-aware footer — see Sidebar Blocks under Patterns.
import { SidebarNav, type NavGroup } from "@pestoui/core"Groups and rows
Every row is a NavNode: an id, a label, and usually an icon. activeId names the current page — the row takes the --sidebar-accent fill and its leading icon takes --selected, the green this system reserves for the selected state. A node with an href renders an anchor; one without renders a button, so a nav with no routing is still semantic. SidebarNav has to sit inside a SidebarProvider, which is where the panel widths and the collapse state live.
Sections
A group is a section. Give it a label for a heading, or set separated to draw a rule above it instead — the two are alternatives, which is what lets one component read both shapes. A rule above the first group would be an edge rather than a divider, so it is skipped. action drops a control beside the heading.
Counts, shortcuts and external rows
badge, shortcut and external all land in the same in-flow trailing slot, so a row can carry more than one without them stacking on top of each other. badge takes a number or a string, shortcut renders as a key cap, and external opens the link in a new tab and marks it with an arrow that steps along its own diagonal on hover. disabled greys the row and stops it being clicked.
Nested groups
A node with children becomes a collapsible branch: the row gets a chevron, the children indent under a guide line, and nesting keeps going for as many levels as the data has. defaultExpandedIds lists the branches open on mount; pass expandedIds with onExpandedChange instead to drive expansion from your own state — restoring it from a route, say.
Icon rail
Collapsing belongs to the shell — Sidebar collapsible="icon" plus a SidebarTrigger — but the nav is what changes shape. Labels, counts and shortcuts drop away, each row keeps its icon and gains a tooltip carrying the label it just lost, and a nested group moves its children into a right-hand flyout rather than losing them, which is what a bare SidebarMenuSub does at this width. The shell pins itself to the viewport, so the frame below stands in for the page; in an app it is the page.
Account block in the footer
SidebarUser is the account row the lg size was always for: avatar, name, one line of secondary text and a trailing affordance, all of it truncating correctly and surviving the collapse to a 36px rail. Put it in SidebarFooter under the nav; the nav itself scrolls in SidebarContent while the footer stays put.
Props
Pesto UI · 117 documented components