SidebarNav
Vertical navigation for application sidebars, with groups, icons and trailing counts.
import { Badge, SidebarNav } from "@merid/react";
export function Example() {
return (
<SidebarNav aria-label="Workspace">
<SidebarNav.Item href="#" icon={<Icon />} active>
Overview
</SidebarNav.Item>
<SidebarNav.Item href="#" icon={<Icon />} trailing={<Badge>12</Badge>}>
Inbox
</SidebarNav.Item>
<SidebarNav.Group label="Projects">
<SidebarNav.Item href="#">Northwind</SidebarNav.Item>
<SidebarNav.Item href="#">Contoso</SidebarNav.Item>
</SidebarNav.Group>
</SidebarNav>
);
}Import
import { SidebarNav } from "@merid/react";SidebarNavItem is also exported on its own and is the same component as SidebarNav.Item.
Anatomy
<SidebarNav aria-label="…">
<SidebarNav.Item href="…" icon={…} trailing={…} active />
<SidebarNav.Group label="…">
<SidebarNav.Item href="…" />
</SidebarNav.Group>
</SidebarNav>SidebarNav(alsoSidebarNav.Root) — a<nav>with a list.SidebarNav.Item— a link wrapped in its own<li>, with optional icon and trailing element.SidebarNav.Group— an<li>with a visible label that names a nested list.
Examples
With a router
Pass your router's link component through as. Extra props are forwarded to it.
import Link from "next/link";
import { usePathname } from "next/navigation";
import { SidebarNav } from "@merid/react";
export function AppNav() {
const pathname = usePathname();
return (
<SidebarNav aria-label="Main">
<SidebarNav.Item as={Link} href="/settings" active={pathname === "/settings"}>
Settings
</SidebarNav.Item>
</SidebarNav>
);
}API reference
SidebarNav
Renders <nav> and accepts all its HTML attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | – | Accessible name of the landmark (required when a page has several navs). |
ref | Ref<HTMLElement> | – | Forwarded ref to the nav element. |
SidebarNav.Item
Renders <a> (or as) inside an <li> and accepts all anchor attributes plus any props for as.
| Prop | Type | Default | Description |
|---|---|---|---|
active | boolean | false | Marks the current page: sets aria-current="page" and the selected style. |
icon | ReactNode | – | Icon shown before the label (decorative). |
trailing | ReactNode | – | Trailing element such as a count Badge. |
as | ElementType | "a" | Element or component to render instead of <a>, e.g. a router Link. |
ref | Ref<HTMLAnchorElement> | – | Forwarded ref to the link. |
SidebarNav.Group
Renders <li> and accepts its HTML attributes except title.
| Prop | Type | Default | Description |
|---|---|---|---|
labelRequired | ReactNode | – | Visible group heading; also labels the nested list. |
Styling
.mrd-sidebar-nav,.mrd-sidebar-nav__list— root and each list, 2px gap between items..mrd-sidebar-nav__item— the link: 36px min height (44px on coarse pointers),--mrd-radius-md..mrd-sidebar-nav__item[aria-current="page"](alsodata-state="active") —--mrd-accent-softfill with--mrd-accent-strongtext..mrd-sidebar-nav__icon,.mrd-sidebar-nav__label,.mrd-sidebar-nav__trailing— icon slot, truncating label, trailing slot..mrd-sidebar-nav__group,.mrd-sidebar-nav__group-label— group spacing and its muted label.
Accessibility
Follows the APG landmark regions guidance for a navigation landmark; there is no widget pattern because items are plain links.
- Name the landmark with
aria-labelwhen the page has more than onenav. - The active link has
aria-current="page". - Each group's nested list is labelled by its visible heading via
aria-labelledby. - Icons are
aria-hidden; the label must carry the meaning.
| Key | Action |
|---|---|
| Tab | Moves focus to the next link. |
| ShiftTab | Moves focus to the previous link. |
| Enter | Follows the focused link. |
Guidelines
Do
Keep labels to one or two words and mark exactly one item active.
Avoid
Put actions such as “New project” buttons in the list; they are not destinations.
Do
Use trailing for counts people act on, like unread items.
Avoid
Rely on the icon alone; the label is always required.