Sidebar Primitive
A composable Core layout primitive for physical side placement, controlled or uncontrolled collapse, accessible toggling, and tokenized regions.
Overview and decision boundary
Use Sidebar Primitive for a persistent collapsible page region while keeping routes, navigation data, menus, permissions, and persistence in consumer code.
Do not add route matching, navigation schemas, persistence, authorization, or account behavior to Core Sidebar.
Installation and imports
Install the editable registry source, or use the matching package entrypoint when the product keeps Nerio as a workspace dependency.
pnpm dlx nerio add sidebar-primitiveimport { SidebarContent, SidebarFooter, SidebarHeader, SidebarInset } from '@nerio-ui/ui';import { Sidebar, SidebarProvider, SidebarRail, SidebarTrigger } from '@nerio-ui/ui/client';Usage
import { SidebarContent, SidebarFooter, SidebarHeader, SidebarInset } from '@nerio-ui/ui';import { Sidebar, SidebarProvider, SidebarRail, SidebarTrigger } from '@nerio-ui/ui/client';<SidebarProvider defaultExpanded side="left"> <Sidebar aria-label="Workspace sidebar"> <SidebarHeader>Workspace</SidebarHeader> <SidebarContent> <nav aria-label="Workspace">...</nav> </SidebarContent> <SidebarFooter>...</SidebarFooter> <SidebarRail label="Toggle workspace sidebar" /> </Sidebar> <SidebarInset> <SidebarTrigger label="Toggle workspace sidebar" /> ... </SidebarInset></SidebarProvider>Variants
| Variant | Purpose |
|---|---|
side: left | right | Side: left | right is part of the public component contract. |
direction: ltr | rtl | Direction: ltr | rtl is part of the public component contract. |
controlled | Controlled is part of the public component contract. |
uncontrolled | Uncontrolled is part of the public component contract. |
comfortable density | Comfortable density is part of the public component contract. |
compact density | Compact density is part of the public component contract. |
Anatomy
| Slot | Purpose |
|---|---|
sidebar-provider | Sidebar Provider slot exposed through data-slot="sidebar-provider". |
sidebar | Complementary aside with physical side and state data. |
sidebar-inner | Sidebar Inner slot exposed through data-slot="sidebar-inner". |
sidebar-header | Sidebar Header slot exposed through data-slot="sidebar-header". |
sidebar-content | Sidebar Content slot exposed through data-slot="sidebar-content". |
sidebar-footer | Sidebar Footer slot exposed through data-slot="sidebar-footer". |
sidebar-rail | Sidebar Rail slot exposed through data-slot="sidebar-rail". |
sidebar-inset | Sidebar Inset slot exposed through data-slot="sidebar-inset". |
sidebar-trigger | Sidebar Trigger slot exposed through data-slot="sidebar-trigger". |
States
| State | Purpose |
|---|---|
Expanded | All static regions and consumer content are available. |
Collapsed | Inner content is inert, hidden, and removed from keyboard interaction. |
Controlled | expanded and onExpandedChange let consumers own state and persistence. |
Motion
- Width and content visibility use the Sidebar transition tokens.
- Reduced-motion preference removes the visible transition without changing state behavior.
Accessibility
- Sidebar renders complementary aside semantics by default; place a labelled navigation landmark inside SidebarContent when the content is navigation.
- SidebarTrigger and SidebarRail require a localized label and expose aria-expanded plus a stable aria-controls relationship.
- SidebarRail is vertically centered and occupies only --n-sidebar-rail-hit-area so adjacent Sidebar and SidebarInset content remains pointer reachable.
- SidebarContent forwards an HTMLDivElement ref, while SidebarInset preserves stable HTMLElement refs for its explicit div or main root.
- Collapsed Sidebar content is inert, hidden, and removed from pointer and keyboard interaction while SidebarRail remains reachable.
- State changes keep focus on the external trigger or rail and do not add roving focus or Arrow-key behavior to consumer navigation.
- Physical left and right placement, explicit text direction, reduced motion, forced colors, compact density, and safe-area padding are supported.
- Persistence and mobile behavior remain consumer-owned; compose Sheet explicitly for mobile navigation so only one interactive tree is rendered.
API
| Prop | Purpose |
|---|---|
SidebarProvider | defaultExpanded, expanded, onExpandedChange, side, direction, and optional sidebarId. |
Sidebar | Client root that reflects provider state through data-state and data-side. |
SidebarHeader / SidebarContent / SidebarFooter / SidebarInset | Server-safe layout regions with native props, stable refs, and an exact div contract for SidebarContent. |
SidebarTrigger / SidebarRail | Named toggle controls with stable focus and ARIA relationships. |
useSidebar | Exposes expanded, setExpanded, toggle, side, direction, and sidebarId. |
Implementation contract
| Contract | Value |
|---|---|
Registry item | sidebar-primitive installs 8 source files into the configured components directory. |
Base UI | No interactive primitive required. |
Registry dependencies | None. |
Package dependencies | @nerio-ui/adapters, clsx, react, tailwind-merge, tailwindcss |
Styling contract
| Contract | Value |
|---|---|
Authoring | Complete, statically detectable Tailwind CSS v4 recipes own component visuals. |
Values | Semantic and component --n-* variables remain the canonical customization layer. |
Overrides | Customizable slots merge consumer className values with tailwindCn so conflicting utilities resolve deterministically. |
Residual CSS | Only shared keyframes and scoped no-Preflight compatibility rules remain; there is no parallel visual selector layer. |
Design notes
- Persistence is consumer-owned. Read storage before choosing controlled state and write from onExpandedChange.
- For mobile, render the shared navigation data inside Sheet instead of turning Sidebar into an AppShell.
- AppSidebar, workspace switching, nested navigation, route matching, account menus, badges, and permissions remain Pro or consumer responsibilities.
Do / do not
Do
Use for a neutral persistent layout region that composes consumer-owned content.
Use controlled state when a product needs localStorage or cookie persistence.
Do not
Do not add route matching, navigation schemas, persistence, authorization, or account behavior to Core Sidebar.
Do not render desktop Sidebar and mobile Sheet as two simultaneously focusable trees.
Related components
Tokens
These are the primary customization points. Override semantic or component tokens instead of changing component source.
| Token | Purpose |
|---|---|
--n-sidebar-width | Public customization point for this component contract. |
--n-sidebar-collapsed-width | Public customization point for this component contract. |
--n-sidebar-inset-gap | Public customization point for this component contract. |
--n-sidebar-region-padding | Public customization point for this component contract. |
--n-sidebar-rail-hit-area | Public customization point for this component contract. |
--n-sidebar-transition-duration | Public customization point for this component contract. |
--n-sidebar-transition-easing | Public customization point for this component contract. |
--n-sidebar-background | Public customization point for this component contract. |
--n-sidebar-foreground | Public customization point for this component contract. |
--n-sidebar-border | Public customization point for this component contract. |
--n-sidebar-control-background | Public customization point for this component contract. |
--n-sidebar-control-background-hover | Public customization point for this component contract. |
--n-sidebar-control-foreground | Public customization point for this component contract. |
--n-motion-hover-duration | Public customization point for this component contract. |
--n-motion-hover-easing | Public customization point for this component contract. |
--n-focus-ring | Public customization point for this component contract. |