Sidebar
- Stable
- WCAG 2.2 evidence
- RTL
Persistent app navigation: a named nav landmark that is 264 px wide (w-sidebar) or a 72 px icon
rail (w-sidebar-collapsed), with a header (workspace switcher and collapse toggle), the main items, a footer and the
user card. Built for wide and medium layouts; on narrow viewports render a NavigationBar instead (the brief's
viewport pattern: expanded from xl, the rail from md, Navigation bar below md). Accessibility: one nav landmark with a name (WCAG 1.3.1, 2.4.1); the optional skip link bypasses it (2.4.1); Tab moves
through the items in visual order (2.4.3); the collapse toggle exposes aria-expanded and keeps focus (4.1.2); in the
rail every item keeps its name and shows it in a tooltip; the main area scrolls inside the sidebar with scroll padding
so focus is not hidden (2.4.11). Consumer duties: a unique landmark name when the page has several navs; render only
one of Sidebar and NavigationBar at a time (hide the other with display: none, which also hides its landmark); the
responsive switch between expanded and rail (control isCollapsed from a media query) and the page skip-link target.
import { Sidebar } from "@nexera-ui/react";- 9
- examples
- 14
- props
- 4
- live controls
- 1
- platform
- 10
- WCAG criteria
- 1
- block uses it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { Sidebar } from "@nexera-ui/react";
<Sidebar />Examples 8
The same examples as Storybook, rendered live. Open Code to copy one.
Collapsed
Figma Collapsed=True: the 72 px rail. Names stay available to assistive technology and show in tooltips.
import { Sidebar } from "@nexera-ui/react";
<Sidebar variant="brand" defaultCollapsed />;
Light
Figma Variant=Light: bg/surface with the nav/* tokens, which follow the theme.
import { Sidebar } from "@nexera-ui/react";
<Sidebar variant="light" />;
Light collapsed
Figma Variant=Light, Collapsed=True.
import { Sidebar } from "@nexera-ui/react";
<Sidebar variant="light" defaultCollapsed />;
Search and copilot recipe
Recipe for the Figma Search and Copilot card frames (sidebar content, not components): a search button as the first item, and a card in the footer render function that the rail hides.
import { Sidebar, SidebarItem } from "@nexera-ui/react";
import { LuLayoutDashboard, LuLifeBuoy, LuSearch, LuSparkles, LuUsers } from "react-icons/lu";
<Sidebar
variant="brand"
footer={({ isCollapsed }) => (
<>
<SidebarItem href="#help" icon={<LuLifeBuoy />} label="Help and support" />
{isCollapsed ? null : (
<div className="flex flex-col gap-2 rounded-xl bg-sidebar-hover px-3.5 py-3 text-sidebar-text">
<span className="flex items-center gap-2 text-body-strong">
<LuSparkles aria-hidden="true" className="size-4" />
Copilot
</span>
<span className="text-body-caption text-sidebar-text-muted">
Ask about leave, payroll or people.
</span>
</div>
)}
</>
)}
>
{
<>
<SidebarItem icon={<LuSearch />} label="Search" />
<SidebarItem href="#dashboard" icon={<LuLayoutDashboard />} label="Dashboard" isCurrent />
<SidebarItem href="#people" icon={<LuUsers />} label="People" />
</>
}
</Sidebar>;
Responsive pattern
The brief's viewport pattern, as the consumer wires it: expanded from xl (1280 px), the rail below. Below md (768 px) render a Navigation bar instead and hide the sidebar with display: none so only one landmark exists.
import { useEffect, useState } from "react";
import { Sidebar } from "@nexera-ui/react";
export function ResponsivePattern() {
const [isCollapsed, setCollapsed] = useState(false);
useEffect(() => {
const query = window.matchMedia("(min-width: 80rem)");
const update = () => {
setCollapsed(!query.matches);
};
update();
query.addEventListener("change", update);
return () => {
query.removeEventListener("change", update);
};
}, []);
return (
<div className="h-[40rem] max-h-[calc(100dvh-3rem)]">
<Sidebar variant="brand" isCollapsed={isCollapsed} onCollapsedChange={setCollapsed} />
</div>
);
}
Skip link
skipLinkHref renders a skip link as the first focusable element (WCAG 2.4.1). Press Tab once to see it.
import { Sidebar } from "@nexera-ui/react";
export function SkipLink() {
return (
<div className="flex h-[40rem] max-h-[calc(100dvh-3rem)] flex-wrap gap-4">
<Sidebar variant="brand" skipLinkHref="#story-main" />
<main id="story-main" tabIndex={-1} className="min-w-32 flex-1 text-body-default">
Main content
</main>
</div>
);
}
Long labels
Long labels wrap when expanded.
import { Sidebar, SidebarItem, SidebarSection } from "@nexera-ui/react";
import { LuCalendarDays, LuChartColumn } from "react-icons/lu";
<Sidebar variant="brand">
{
<>
<SidebarItem
href="#leave"
icon={<LuCalendarDays />}
label="Leave, attendance and time-off requests"
isCurrent
/>
<SidebarSection label="Reports and analytics for managers">
<SidebarItem
href="#reports"
icon={<LuChartColumn />}
label="Headcount reports"
badge="12"
/>
</SidebarSection>
</>
}
</Sidebar>;
Right to left
Right to left: the sidebar sits on the right, the border and indicator flip, tooltips open to the left.
import { Sidebar } from "@nexera-ui/react";
<Sidebar variant="brand" />;
Props 14
Press "Try it" on a card to load that prop into the playground.
14 props shown
variantNexera"brand" | "light"Colour scheme: brand (dark sidebar/* tokens, the same in light and dark themes) or light
(bg/surface with the nav/* tokens, which follow the theme). Parts inside take it from the sidebar.
isCollapsedNexerabooleanWhether the sidebar shows the 72 px icon rail, controlled. Use with onCollapsedChange.
defaultCollapsedNexerabooleanWhether the sidebar starts as the rail (uncontrolled).
onCollapsedChangeNexera(isCollapsed: boolean) => voidCalled when the toggle (or a group item pressed in the rail) collapses or expands the sidebar.
headerNexeraReactNodeFigma Header content before the collapse toggle, usually a WorkspaceSwitcher.
childrenNexeraSidebarSlotMain navigation: SidebarItem, SidebarSection, search. It scrolls on
its own when it does not fit. A function receives { isCollapsed }.
footerNexeraSidebarSlotContent pinned to the bottom above the divider. A function
receives { isCollapsed }, for content such as the Copilot card that the rail hides.
userCardNexeraReactNodeFigma Sidebar user card slot under the divider: a SidebarUserCard. The divider is drawn when it is set.
isCollapsibleNexerabooleanShow the collapse toggle in the header. It carries
aria-expanded and controls the sidebar.
toggleLabelNexerastringAccessible name of the collapse toggle. It stays the same while aria-expanded announces the state. Translate it.
skipLinkHrefNexerastringTarget of an optional skip link rendered as the first focusable element, for example "#main" (WCAG 2.4.1). The link
is visually hidden until focused. Give the target tabIndex={-1} so focus lands on it.
skipLinkLabelNexerastringText of the skip link. Translate it.
aria-labelNexerastringName of the navigation landmark. Every landmark on a page needs a unique name; use aria-labelledby to point at a
visible heading instead. Translate it.
aria-labelledbyNexerastringId of a visible element that names the landmark; replaces aria-label.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
6 direct · 4 supporting- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.3.2Meaningful SequenceLevel A · supporting test
- 1.4.10ReflowLevel AA · supporting test
- 2.1.1KeyboardLevel A · tested directly
- 2.3.3Animation from InteractionsLevel AAA · supporting test
- 2.4.1Bypass BlocksLevel A · tested directly
- 2.4.3Focus OrderLevel A · tested directly
- 2.4.4Link Purpose (In Context)Level A · tested directly
- 2.4.11Focus Not Obscured (Minimum)Level AA · supporting test
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
data-hovereddata-pressed
<Sidebar className="data-hovered:opacity-90 shadow-sm" />Used in blocks
Related components
- BreadcrumbShows where the current page sits in the hierarchy and links back to each level.
- BreadcrumbItemOne level of a `Breadcrumb` trail; use it only inside `Breadcrumb`.
- CursorPagerA previous and next pair for data without stable page numbers, such as an activity log or a cursor API.
- FABThe main action of a mobile screen: a floating button with a `nav/active-bg` fill and a large shadow.
- FeedStatusThe status line at the bottom of an infinite feed or a lazy list: "Loading more", "You have reached the end" or "Could not load more" with a retry action.
- JumpToPageA small "Go to page [48] of 120" control for long paged lists.
- NavigationBarThe bottom bar of a mobile layout: a named `nav` landmark with a list of three to five `NavItem`s.
- NavItemOne destination or action of a `NavigationBar`.