UI.MD

Nav

A navigation list of links with icon slots and a sliding active pill, horizontal or vertical.

Installation

$
npx shadcn@latest add https://ui.mattdowney.com/r/nav.json

Usage

import { Nav, NavList, NavSection, NavLabel, NavItem } from "@/components/ui/nav"
import { resolveActiveHref } from "@/components/ui/nav-active"

Examples

Default

Horizontal, the top nav pattern. Pass href and handle the click yourself, or swap in a router link with render.

Sizes

size steps items along the control scale: sm (h-7, 12px), default (h-8, 13px), lg (h-10, 14px). One prop on Nav sizes every item, label, and count chip, in both orientations.

With Count

Pass a count to add a counter chip, the same one a TabsTrigger carries: solid accent on the active item, a neutral tint on the rest. size on Nav scales it with the items. Cap large counts at "99+" so a growing number cannot widen the item.

In a header

Nav shares the kit's control heights, so a header lines up when every control in the row is set to the same size: sm is 28px, default 32px, lg 40px, matching Button tier for tier.

Small
Default
Large

Vertical

orientation="vertical" groups items into NavSections with a NavLabel heading, the sidebar pattern.

Vertical with Count

A count sits at the trailing edge of a vertical item, not against the label, so a column of numbers reads as a column. That needs a width on the Nav: items size to their own content, and there is no shared trailing edge to push a count to until you give them one.

Mobile

A composition, not a separate component: a Button opens a Drawer containing the vertical Nav.

API Reference

PropTypeDefaultDescription
orientationNav"horizontal" | "vertical""horizontal"Lays out the list as a row (top nav) or a column (sidebar) and sizes items to match.
sizeNav"sm" | "default" | "lg""default"Steps items and labels along the control scale, from sm (h-7, 12px type) up to lg (h-10, 14px type).
iconNavItemReact.ReactNode-Leading icon rendered before the item's label, e.g. <Star />.
countNavItemReact.ReactNode-Counter chip after the label, matching the TabsTrigger chip tier for tier. Inline in a horizontal nav, pushed to the trailing edge in a vertical one. Add an aria-label where a bare number needs context (aria-label="Inbox, 12 unread").
activeNavItemboolean-Marks the item as the current page. Sets aria-current="page" and drives the sliding pill.
renderNavItemReact.ReactElement-Swaps the default <a> for another element, e.g. render={<Link href="/dashboard" />} to compose with a router.