Skip to content
Nizivo UI

Breadcrumb

Where the page sits in the site, as a navigation landmark of links around an ordered list. The last level is the current page, as text with aria-current="page"; a router's link goes in through each item's render.

Server component, no client JS

Import

import { Breadcrumb } from '@nizivo/react/breadcrumb';

// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css
import '@nizivo/react/css/breadcrumb.css';

Static or interactive

Server. It ships no client JavaScript when a Server Component renders it, in every Next.js build mode; its code is bundled only when a client component (a 'use client' file) imports it. Its links are plain <a> elements; a router link passed through render is the router's own code. How to choose.

In a client app (a Vite or React Router single-page app, or a 'use client' file) it is an ordinary React component: event handlers, refs and state work as usual. In a client app.

Long trails

Nothing collapses in 0.2: every level stays a link, in the DOM and in the Tab order, and a trail longer than its container wraps onto a second line. Hiding middle levels behind an ellipsis needs a menu to reach them, which is client code, so it is not built and no level is ever unreachable.

Examples

Basic

Figma's trail: two links and the current page, which is text with aria-current="page".

import type { JSX } from 'react';
import { Breadcrumb } from '@nizivo/react/breadcrumb';

export function BreadcrumbExample(): JSX.Element {
  return (
    <Breadcrumb.Root>
      <Breadcrumb.Item href="/products">Products</Breadcrumb.Item>
      <Breadcrumb.Item href="/products/mugs">Mugs &amp; cups</Breadcrumb.Item>
      <Breadcrumb.Item current>Stoneware mug</Breadcrumb.Item>
    </Breadcrumb.Root>
  );
}

Router

A router's link goes in through render; a separator replaces every chevron.

import type { ComponentPropsWithRef, JSX } from 'react';
import { Breadcrumb } from '@nizivo/react/breadcrumb';

/** Stands in for a router link (Next `Link`, React Router `Link`). */
function Link({ children, ...props }: ComponentPropsWithRef<'a'>): JSX.Element {
  return <a {...props}>{children}</a>;
}

export function BreadcrumbRouterExample(): JSX.Element {
  return (
    <Breadcrumb.Root separator="/" labels={{ nav: 'You are here' }}>
      <Breadcrumb.Item render={<Link href="/orders" />}>Orders</Breadcrumb.Item>
      <Breadcrumb.Item render={<Link href="/orders/1042" />}>#1042</Breadcrumb.Item>
      <Breadcrumb.Item current>Refund</Breadcrumb.Item>
    </Breadcrumb.Root>
  );
}

Parts

Use the parts through the namespace (Breadcrumb.Root) or by their flat names (BreadcrumbRoot); both work from Server Components.

Server component Renders data-slot="breadcrumb" with the class nz-breadcrumb. Props: BreadcrumbRootProps. Source: src/breadcrumb/breadcrumb.tsx.

NameTypeDefaultDescription
childrenReactNode–

Breadcrumb.Item elements, the root of the site first.

labelsPartial<BreadcrumbLabels>–

An aria-label wins over labels.nav.

renderRenderProp<Record<string, never>>–

Must render a navigation landmark (a <nav>, or an element with role="navigation").

separatorReactNode–

Replaces the chevron between levels on every Breadcrumb.Item that sets none. It is decorative (aria-hidden), so a character such as "/" is fine. It is not mirrored under dir="rtl".

  • Also takes every attribute of <nav>; children is redeclared above. (ComponentPropsWithRef<"nav">)

render takes an element, or, in client code, a function of the props and no state.

Server component Renders data-slot="breadcrumb-item" with the class nz-breadcrumb-item. Props: BreadcrumbItemProps. Source: src/breadcrumb/breadcrumb.tsx.

NameTypeDefaultDescription
childrenrequiredReactNode–
currentbooleanfalse

The page being shown: renders text with aria-current="page" instead of a link (Figma: the last item, Label/M Strong).

renderRenderProp<BreadcrumbItemState>–

The router's link, for example render={<Link href="/products" />}. It must render an <a>. Ignored on the current item, which is not a link; so are the other anchor props (href), while className goes on whichever element renders.

separatorReactNodethe chevron glyph

The mark before this level (none is drawn before the first). Only the default chevron mirrors under dir="rtl".

  • Also takes every attribute of <a> except children. (Omit<ComponentPropsWithRef<"a">, "children">)

render takes an element, or, in client code, a function of the props and this state (BreadcrumbItemState):

NameTypeDefaultDescription
currentrequiredboolean–

Other exports

BreadcrumbItemProps type

import type { BreadcrumbItemProps } from '@nizivo/react/breadcrumb'

The props of Breadcrumb.Item, listed above.

BreadcrumbItemState type

import type { BreadcrumbItemState } from '@nizivo/react/breadcrumb'

The render state of Breadcrumb.Item, listed above.

BreadcrumbLabels type

import type { BreadcrumbLabels } from '@nizivo/react/breadcrumb'

Breadcrumb's own strings (§16.4): serialisable, English defaults.

NameTypeDefaultDescription
navrequiredstring'Breadcrumb'

The landmark's name.

BreadcrumbRootProps type

import type { BreadcrumbRootProps } from '@nizivo/react/breadcrumb'

The props of Breadcrumb.Root, listed above.

Data attributes and slots

Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.

SlotRendered byData attributes
breadcrumb
.nz-breadcrumb
Breadcrumb.Root
root (takes render)
none
breadcrumb-current
.nz-breadcrumb-current
Breadcrumb.Item
inner element (CSS only)
none
breadcrumb-item
.nz-breadcrumb-item
Breadcrumb.Item
root (takes render)
none
breadcrumb-link
.nz-breadcrumb-link
Breadcrumb.Item
inner element (CSS only); interactive, focus: self
none
breadcrumb-list
.nz-breadcrumb-list
Breadcrumb.Root
inner element (CSS only)
none
breadcrumb-separator
.nz-breadcrumb-separator
Breadcrumb.Item
inner element (CSS only)
  • data-mirror: "rtl"
    Mirror under :dir(rtl)

CSS hooks

Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.

HookDefaultRead as
--nz-breadcrumb-current-fg--nz-text-primary
  • color on .nz-breadcrumb-current
--nz-breadcrumb-gap--nz-space-1-5
  • gap on .nz-breadcrumb-item
  • gap on .nz-breadcrumb-list
--nz-breadcrumb-link-fg--nz-text-secondary
  • color on .nz-breadcrumb-link
--nz-breadcrumb-link-fg-hover--nz-text-primary
  • color on .nz-breadcrumb-link:hover @media (hover: hover)
--nz-breadcrumb-separator-fg--nz-icon-secondary
  • color on .nz-breadcrumb-separator

Layers: nz.components. Classes: .nz-breadcrumb-current, .nz-breadcrumb-item, .nz-breadcrumb-link, .nz-breadcrumb-list, .nz-breadcrumb-separator.

Tokens this stylesheet reads (14)

--nz-duration-fast, --nz-easing-standard, --nz-font-label-m, --nz-font-label-m-strong-font-weight, --nz-icon-secondary, --nz-radius-xs, --nz-size-icon-sm, --nz-space-1, --nz-space-1-5, --nz-space-6, --nz-text-primary, --nz-text-secondary, --nz-tracking-label-m, --nz-tracking-label-m-strong

Accessibility

These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:

  • breadcrumb-link: draws the focus outline itself on keyboard focus.

Keyboard

KeysAction
Tab, Shift+TabMoves between the links, as on any link. The current page is text, not a Tab stop.
EnterFollows the focused link.

Source: native <a> elements in a <nav> (breadcrumb.tsx, no client code). The browser lane (test/browser/breadcrumb.spec.tsx) checks Tab through the links in order, the current page not being a stop, and the focus outline.

Figma

Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.

On this page