Skip to content
Component installs need the registry setup
VegaStack Design

Breadcrumb

The path to the current resource as a hierarchy of links — separators, the current page, a collapsed ellipsis, and any routing library's link through render.

Status
stable
Since
0.1.0
Accessibility pattern
APG breadcrumb

Last updated

Install

Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Breadcrumb before installation. Retain the digest printed by this command.

pnpm exec vegastack-design verify breadcrumb

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/breadcrumb

Run the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.

Usage

import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";

<Breadcrumb>
  <BreadcrumbList>
    <BreadcrumbItem>
      <BreadcrumbLink render={<a href="/" />}>Home</BreadcrumbLink>
    </BreadcrumbItem>
    <BreadcrumbSeparator />
    <BreadcrumbItem>
      <BreadcrumbLink render={<a href="/components" />}>
        Components
      </BreadcrumbLink>
    </BreadcrumbItem>
    <BreadcrumbSeparator />
    <BreadcrumbItem>
      <BreadcrumbPage>Breadcrumb</BreadcrumbPage>
    </BreadcrumbItem>
  </BreadcrumbList>
</Breadcrumb>;

Anatomy

Breadcrumb — data-slot="breadcrumb"
BreadcrumbEllipsis — data-slot="breadcrumb-ellipsis"
BreadcrumbItem — data-slot="breadcrumb-item"
BreadcrumbLink
BreadcrumbList — data-slot="breadcrumb-list"
BreadcrumbPage — data-slot="breadcrumb-page"
BreadcrumbSeparator — data-slot="breadcrumb-separator"

Examples

Composition

Breadcrumb is a nav labelled breadcrumb; BreadcrumbList is the ol inside it. Every segment is a BreadcrumbItem, separators sit between items as their own li, and the last segment is a BreadcrumbPage rather than a link.

Breadcrumb
└── BreadcrumbList
    ├── BreadcrumbItem
    │   └── BreadcrumbLink
    ├── BreadcrumbSeparator
    ├── BreadcrumbItem
    │   └── BreadcrumbLink
    ├── BreadcrumbSeparator
    └── BreadcrumbItem
        └── BreadcrumbPage

Basic

A home link, a section link and the current page. BreadcrumbLink renders an a by default, so href goes straight on it.

Custom separator

Pass children to BreadcrumbSeparator to replace the default chevron. The separator is role="presentation" and aria-hidden, so whatever it renders is decoration and never enters the accessible name of the trail.

Compose BreadcrumbItem with a DropdownMenu when a segment has siblings the reader can switch to. The trigger is a real button, so it is reachable by keyboard and announces its expanded state.

Collapsed

BreadcrumbEllipsis stands in for the segments a long trail hides. It is role="presentation" and aria-hidden — decoration, not a control — so when the hidden segments must stay reachable, wrap it in a real trigger, as the example at the top of this page does with a Button size="icon-sm" and a DropdownMenu.

render on BreadcrumbLink replaces the element it renders, which is how a routing library's link gets the breadcrumb's styling and its data-slot. In a Next.js app that is render={<Link href="/docs" />}; this page has no router, so the render target below is a plain anchor.

RTL

Wrap the subtree in DirectionProvider direction="rtl" so Base UI resolves logical sides, and set dir on the elements that portal out of it. The default separator carries rtl:rotate-180, so the chevron points the way the text reads; a dropdown inside the trail aligns to end instead of start.

API Reference

Breadcrumb adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

BreadcrumbList adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

BreadcrumbItem adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

BreadcrumbLink adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

BreadcrumbPage adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

BreadcrumbSeparator adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

BreadcrumbEllipsis adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).

Accessibility

  • Breadcrumb is a nav with aria-label="breadcrumb" wrapping an ordered list, which is the APG breadcrumb pattern: assistive technology reads the trail as a landmark and the segments in order.
  • BreadcrumbPage is the current page. It carries aria-current="page" and aria-disabled, so it is announced as the destination the reader is already on rather than as a link they can follow.
  • BreadcrumbSeparator and BreadcrumbEllipsis are both role="presentation" and aria-hidden — neither is a control and neither adds words to the trail. A collapsed trail that must stay navigable puts the ellipsis inside a real trigger with its own accessible name.
  • Focus is the global 2px :focus-visible outline from base.css; nothing in the trail paints a ring glow.
ContractStates tested
Behaviourdefault, current, collapsed
Accessibilitynative-or-base-ui-semantics, browser-accessibility-test, current, presentation-only-decoration
Visualdefault, hover, focus-visible

Do / Don't

Do
Use a Breadcrumb to show where the current page sits in a hierarchy, ending in a BreadcrumbPage the reader cannot click.
Don't
Use a Breadcrumb as primary navigation, or leave collapsed segments reachable only through the aria-hidden ellipsis.

Deviations

Upstream's file plus packages/ui/upstream/patches/breadcrumb.patch. Every hunk:

  • A11Y-2 — BreadcrumbLink gains relative after:absolute after:inset-x-0 after:-inset-y-1 after:content-['']. Its visible box is one line of 14px text, measured 20px tall by the geometry lane — 4px under the floor — and a breadcrumb is a row of navigation controls, not prose, so SC 2.5.8's inline-text exception does not reach it. The invisible area grows the target on the block axis only, so two adjacent crumbs cannot fight for a pixel; nothing moves and nothing paints. The ellipsis is untouched: it is role="presentation" aria-hidden, and upstream's own Collapsed example wraps it in a Button size="icon-sm" that is already 32px. breadcrumb.test.tsx asserts that shape, so a future upstream that makes the ellipsis itself the control fails here.
  • API-16 — "use client" is added. Upstream ships none, but BreadcrumbLink calls useRender, which calls React.useRef internally; under the react-server condition that hook is undefined, so a Server Component rendering a breadcrumb link throws on import. The boundary belongs at the lowest interactive leaf, and this file is it.
  • DOC-2 — import { cn } from "cn" becomes @vegastack/design; our npm layer owns cn.
  • DOC-1 — prettier reflow plus the registry-stamp.mjs provenance header the three-copy model needs; no decision of its own.

On this page