Overview
One place to find your way.
Try a page, open a group, or collapse the sidebar.
A home for your navigation. Collapsible groups, an icon rail, and a mobile drawer, built with Base UI.
import {
Sidebar, SidebarProvider, SidebarContent,
SidebarGroup, SidebarMenu, SidebarMenuItem,
SidebarMenuButton, SidebarTrigger,
} from "@tulipfarm/sprout/sidebar";
<SidebarProvider className="min-h-dvh">
<Sidebar label="Main navigation">
<SidebarContent>
<SidebarGroup>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
icon={<HomeIcon />}
nativeButton={false}
render={<a href="/overview" />}
isActive
>
Overview
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
</Sidebar>
<main className="min-w-0 flex-1">
<SidebarTrigger />
{children}
</main>
</SidebarProvider>Supply icons from your own icon library. Use the render prop with your router's link and nativeButton= for navigation. Use a normal button for actions.
SidebarProvider owns open state. Sidebar handles the panel and mobile drawer. SidebarHeader and SidebarFooter stay visible while SidebarContent scrolls. Put aSidebarInput in the header to search or filter the menu; it is an Input on the canvas color.
SidebarGroup and SidebarGroupLabel organize a SidebarMenu. Each SidebarMenuItem contains a SidebarMenuButton, with optional icon, badge, isActive, and tooltip props. Give complex labels an explicit aria-label or tooltip.
SidebarMenuAction adds a separate action. SidebarSection combines a navigation link with an independent disclosure button. Put SidebarMenuSubItem and SidebarMenuSubButton inside it. For a custom composition, use SidebarMenuSub directly.
<SidebarSection
label="Projects"
icon={<FolderIcon />}
nativeButton={false}
render={<a href="/projects" />}
defaultOpen
>
<SidebarMenuSubItem>
<SidebarMenuSubButton
nativeButton={false}
render={<a href="/projects/website" />}
>
Website refresh
</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarSection>The label navigates; the arrow expands. Collapsing the whole sidebar hides nested links without forgetting which sections were open. Sections also accept open and onOpenChange.
Sidebar supports side="left" or "right", and collapsible="icon", "offcanvas", or "none". Put a right sidebar after your content in the DOM. The provider follows its container's height; give it a viewport height or a bounded preview height.
SidebarProvider accepts defaultOpen, or controlled open / onOpenChange. Mobile state is separate through openMobile / onOpenMobileChange. useSidebar() exposes these states, their setters, isMobile, state, and toggleSidebar().
Saving preferences belongs to the application. This preview stores its open state in local storage; the package never writes cookies or storage. For server rendering, pass your saved preference as defaultOpen.
Below 768px, collapsible sidebars become modal drawers. Base UI handles focus trapping, Escape dismissal, and scroll locking. Links close the drawer; custom navigation handlers can call setOpenMobile(false). A visible Close action is always provided.
Tab follows normal link order. Enter activates links; Enter or Space toggles disclosures. Icon-only items keep accessible names and show tooltips on hover or focus. Set keyboardShortcut= on the provider to disable Cmd/Ctrl+B. The shortcut does not run inside editable fields.
SidebarMenuSkeleton provides a static loading row. SidebarSeparator draws an inset line between groups. Your application supplies loading announcements, empty messages, error messages, and retry actions. Routes, permissions, workspace switchers, account menus, and data fetching are not part of the package.