Sidebar Item
- Stable
- WCAG 2.2 evidence
- RTL
One entry of a Sidebar. With href it is a React Aria Link (aria-current="page" when
isCurrent); without href it is a Button; with SidebarSubItem children it is a disclosure button (aria-expanded)
that shows them below. The Figma Variant and Collapsed properties come from the enclosing Sidebar. Accessibility: Tab moves between items, Enter activates links (Enter and Space buttons), keyboard focus shows the
Figma 2 px focus ring (WCAG 2.1.1, 2.4.7). The current item is announced (aria-current) and shown by more than colour:
bold label, indicator bar (kept in the collapsed rail) and fill (1.4.1). In the collapsed rail the label stays the
accessible name (visually hidden) and a tooltip shows it on hover and focus (1.4.13). Rows are at least 40 px tall
(2.5.8). Consumer duties: labels that match page titles; isCurrent on exactly one destination.
import { SidebarItem } from "@nexera-ui/react";- 6
- examples
- 30
- props
- 6
- live controls
- 1
- platform
- 12
- 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 { SidebarItem } from "@nexera-ui/react";
<SidebarItem />Examples 5
The same examples as Storybook, rendered live. Open Code to copy one.
States brand
Figma State: Selected (isCurrent), Default, with badge, Disabled, a button item and a group with sub-items.
import { type ReactNode } from "react";
import { Sidebar, SidebarItem, SidebarSubItem } from "@nexera-ui/react";
import {
LuCalendarDays,
LuClipboardCheck,
LuLayoutDashboard,
LuSearch,
LuUsers,
} from "react-icons/lu";
function Frame({
children,
variant = "brand",
isCollapsed = false,
}: {
children: ReactNode;
variant?: "brand" | "light";
isCollapsed?: boolean;
}) {
return (
<Sidebar
variant={variant}
isCollapsed={isCollapsed}
isCollapsible={false}
aria-label={`${variant} ${isCollapsed ? "rail" : "sidebar"}`}
className="h-auto"
>
{children}
</Sidebar>
);
}
function States({ variant, isCollapsed }: { variant: "brand" | "light"; isCollapsed: boolean }) {
return (
<Frame variant={variant} isCollapsed={isCollapsed}>
<SidebarItem href="#dashboard" icon={<LuLayoutDashboard />} label="Dashboard" isCurrent />
<SidebarItem href="#leave" icon={<LuCalendarDays />} label="Leave" />
<SidebarItem
href="#approvals"
icon={<LuClipboardCheck />}
label="Approvals"
badge="3"
badgeLabel="3 pending"
/>
<SidebarItem href="#reports" icon={<LuUsers />} label="Reports" isDisabled />
<SidebarItem icon={<LuSearch />} label="Search" />
<SidebarItem icon={<LuUsers />} label="People" defaultExpanded>
<SidebarSubItem href="#directory" label="Directory" />
<SidebarSubItem href="#teams" label="Teams" />
</SidebarItem>
</Frame>
);
}
export function StatesBrand() {
return <States variant="brand" isCollapsed={false} />;
}
States light
Figma Variant=Light.
import { type ReactNode } from "react";
import { Sidebar, SidebarItem, SidebarSubItem } from "@nexera-ui/react";
import {
LuCalendarDays,
LuClipboardCheck,
LuLayoutDashboard,
LuSearch,
LuUsers,
} from "react-icons/lu";
function Frame({
children,
variant = "brand",
isCollapsed = false,
}: {
children: ReactNode;
variant?: "brand" | "light";
isCollapsed?: boolean;
}) {
return (
<Sidebar
variant={variant}
isCollapsed={isCollapsed}
isCollapsible={false}
aria-label={`${variant} ${isCollapsed ? "rail" : "sidebar"}`}
className="h-auto"
>
{children}
</Sidebar>
);
}
function States({ variant, isCollapsed }: { variant: "brand" | "light"; isCollapsed: boolean }) {
return (
<Frame variant={variant} isCollapsed={isCollapsed}>
<SidebarItem href="#dashboard" icon={<LuLayoutDashboard />} label="Dashboard" isCurrent />
<SidebarItem href="#leave" icon={<LuCalendarDays />} label="Leave" />
<SidebarItem
href="#approvals"
icon={<LuClipboardCheck />}
label="Approvals"
badge="3"
badgeLabel="3 pending"
/>
<SidebarItem href="#reports" icon={<LuUsers />} label="Reports" isDisabled />
<SidebarItem icon={<LuSearch />} label="Search" />
<SidebarItem icon={<LuUsers />} label="People" defaultExpanded>
<SidebarSubItem href="#directory" label="Directory" />
<SidebarSubItem href="#teams" label="Teams" />
</SidebarItem>
</Frame>
);
}
export function StatesLight() {
return <States variant="light" isCollapsed={false} />;
}
Collapsed
Figma Collapsed=True: icon only; focus or hover an item for its tooltip. The badge becomes a dot.
import { type ReactNode } from "react";
import { Sidebar, SidebarItem, SidebarSubItem } from "@nexera-ui/react";
import {
LuCalendarDays,
LuClipboardCheck,
LuLayoutDashboard,
LuSearch,
LuUsers,
} from "react-icons/lu";
function Frame({
children,
variant = "brand",
isCollapsed = false,
}: {
children: ReactNode;
variant?: "brand" | "light";
isCollapsed?: boolean;
}) {
return (
<Sidebar
variant={variant}
isCollapsed={isCollapsed}
isCollapsible={false}
aria-label={`${variant} ${isCollapsed ? "rail" : "sidebar"}`}
className="h-auto"
>
{children}
</Sidebar>
);
}
function States({ variant, isCollapsed }: { variant: "brand" | "light"; isCollapsed: boolean }) {
return (
<Frame variant={variant} isCollapsed={isCollapsed}>
<SidebarItem href="#dashboard" icon={<LuLayoutDashboard />} label="Dashboard" isCurrent />
<SidebarItem href="#leave" icon={<LuCalendarDays />} label="Leave" />
<SidebarItem
href="#approvals"
icon={<LuClipboardCheck />}
label="Approvals"
badge="3"
badgeLabel="3 pending"
/>
<SidebarItem href="#reports" icon={<LuUsers />} label="Reports" isDisabled />
<SidebarItem icon={<LuSearch />} label="Search" />
<SidebarItem icon={<LuUsers />} label="People" defaultExpanded>
<SidebarSubItem href="#directory" label="Directory" />
<SidebarSubItem href="#teams" label="Teams" />
</SidebarItem>
</Frame>
);
}
export function Collapsed() {
return (
<div className="flex flex-wrap gap-4">
<States variant="brand" isCollapsed />
<States variant="light" isCollapsed />
</div>
);
}
Long label
Long labels wrap instead of truncating.
import { SidebarItem } from "@nexera-ui/react";
import { LuLayoutDashboard } from "react-icons/lu";
<SidebarItem
href="#dashboard"
icon={<LuLayoutDashboard />}
label="Leave, attendance and time-off requests"
badge="12"
/>;
Right to left
Right to left: the indicator and badge move to the other side.
import { type ReactNode } from "react";
import { Sidebar, SidebarItem, SidebarSubItem } from "@nexera-ui/react";
import {
LuCalendarDays,
LuClipboardCheck,
LuLayoutDashboard,
LuSearch,
LuUsers,
} from "react-icons/lu";
function Frame({
children,
variant = "brand",
isCollapsed = false,
}: {
children: ReactNode;
variant?: "brand" | "light";
isCollapsed?: boolean;
}) {
return (
<Sidebar
variant={variant}
isCollapsed={isCollapsed}
isCollapsible={false}
aria-label={`${variant} ${isCollapsed ? "rail" : "sidebar"}`}
className="h-auto"
>
{children}
</Sidebar>
);
}
function States({ variant, isCollapsed }: { variant: "brand" | "light"; isCollapsed: boolean }) {
return (
<Frame variant={variant} isCollapsed={isCollapsed}>
<SidebarItem href="#dashboard" icon={<LuLayoutDashboard />} label="Dashboard" isCurrent />
<SidebarItem href="#leave" icon={<LuCalendarDays />} label="Leave" />
<SidebarItem
href="#approvals"
icon={<LuClipboardCheck />}
label="Approvals"
badge="3"
badgeLabel="3 pending"
/>
<SidebarItem href="#reports" icon={<LuUsers />} label="Reports" isDisabled />
<SidebarItem icon={<LuSearch />} label="Search" />
<SidebarItem icon={<LuUsers />} label="People" defaultExpanded>
<SidebarSubItem href="#directory" label="Directory" />
<SidebarSubItem href="#teams" label="Teams" />
</SidebarItem>
</Frame>
);
}
export function RightToLeft() {
return <States variant="brand" isCollapsed={false} />;
}
Props 30
Press "Try it" on a card to load that prop into the playground.
30 props shown
icon*NexeraReactNodeIcon, 20 px. Required: it is all the collapsed
rail shows. Decorative: the label names the item.
label*NexeraReactNodeVisible label and accessible name. One or two words matching the page title. It wraps when expanded; in the collapsed rail it is visually hidden and shown in a tooltip.
badgeNexeraReactNodeCount or short status, shown when provided: a pill when expanded, an 8 px dot in the collapsed rail. Its text is part of the accessible name ("Approvals 3").
badgeLabelNexerastringText announced instead of badge, for example "3 pending" (the visible badge is then hidden from assistive
technology). Translate it for your locale.
isCurrentNexerabooleanThe item is the current page or section. Links get aria-current="page"; buttons and
group triggers get aria-current="true". Shown by fill, bold label (expanded) and the indicator bar.
classNameNexerastringExtra classes for the row, merged last so they win over the defaults.
styleNexeraCSSPropertiesInline style for the row.
childrenNexeraReactNodeOnly for group items.
SidebarSubItem elements. Makes the row a disclosure button with the Figma chevron; the sub-items show while it is
expanded and are hidden in the collapsed rail (pressing the row there expands the sidebar and the group).
isExpandedNexerabooleanOnly for group items.
Whether the sub-items are shown (controlled). Use with onExpandedChange.
defaultExpandedNexerabooleanOnly for group items. Whether the sub-items are shown on first render (uncontrolled). Expand the group that holds the current page.
onExpandedChangeNexera(isExpanded: boolean) => voidOnly for group items. Called when the group expands or collapses.
idReact AriastringThe element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).
isDisabledReact AriabooleanWhether the link is disabled.
autoFocusReact AriabooleanWhether the element should receive focus on render.
onPressReact Aria(e: PressEvent) => voidHandler that is called when the press is released over the target.
onPressStartReact Aria(e: PressEvent) => voidHandler that is called when a press interaction starts.
onPressEndReact Aria(e: PressEvent) => voidHandler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressChangeReact Aria(isPressed: boolean) => voidHandler that is called when the press state changes.
onPressUpReact Aria(e: PressEvent) => voidHandler that is called when a press is released over the target, regardless of whether it started on the target or not.
aria-labelReact AriastringDefines a string value that labels the current element.
aria-labelledbyReact AriastringIdentifies the element (or elements) that labels the current element.
aria-describedbyReact AriastringIdentifies the element (or elements) that describes the object.
hrefReact AriastringA URL to link to. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#href). A group row is a button; link its first page from a sub-item.
hrefLangReact AriastringHints at the human language of the linked URL. See[MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#hreflang).
targetReact AriaHTMLAttributeAnchorTargetThe target window for the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#target).
relReact AriastringThe relationship between the linked resource and the current page. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel).
downloadReact Ariastring | booleanCauses the browser to download the linked URL. A string may be provided to suggest a file name. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#download).
pingReact AriastringA space-separated list of URLs to ping when the link is followed. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#ping).
referrerPolicyReact Aria"" | "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url"How much of the referrer to send when following the link. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#referrerpolicy).
routerOptionsReact ArianeverOptions for the configured client side router.
* 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
8 direct · 4 supporting- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.3.2Meaningful SequenceLevel A · supporting test
- 1.4.1Use of ColorLevel A · supporting test
- 1.4.4Resize TextLevel AA · supporting test
- 1.4.12Text SpacingLevel AA · supporting test
- 1.4.13Content on Hover or FocusLevel AA · tested directly
- 2.1.1KeyboardLevel A · tested directly
- 2.4.3Focus OrderLevel A · tested directly
- 2.4.4Link Purpose (In Context)Level A · tested directly
- 2.4.7Focus VisibleLevel AA · tested directly
- 2.5.3Label in NameLevel A · tested directly
- 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-currentdata-disableddata-hovereddata-pressed
<SidebarItem className="data-current: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`.