Skip to content
Primitiv home
Framework
Consumption mode

Breadcrumb

stableSource Figma

A WAI-ARIA breadcrumb trail — a nav landmark wrapping an ordered list of ancestor-page links, ending with the current page.

Playground

Density

Preview

Size
import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from "@/components/ui/breadcrumb";
<Breadcrumb>  <BreadcrumbList>    <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator />    <BreadcrumbItem><BreadcrumbLink href="/components">Components</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator />    <BreadcrumbItem><BreadcrumbPage>Breadcrumb</BreadcrumbPage></BreadcrumbItem>  </BreadcrumbList></Breadcrumb>

Density is set by a data-density ancestor — the Context system, not a Breadcrumb prop.

Installation

npx primitiv add breadcrumb

Import

import { Breadcrumb } from "@/components/ui/breadcrumb";

Copied into your project as .primitiv-breadcrumb — you own the file afterwards, so upgrades are opt-in.

Headless mode installs the npm package instead: @primitiv-ui/react

Anatomy

<Breadcrumb>  <BreadcrumbList>    <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator />    <BreadcrumbItem><BreadcrumbLink href="/components">Components</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator />    <BreadcrumbItem><BreadcrumbPage>Breadcrumb</BreadcrumbPage></BreadcrumbItem>  </BreadcrumbList></Breadcrumb>

Props

Breadcrumb.Root

Extends HTMLElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultFromDescription
size"xs" | "sm" | "md" | "lg" | "xl"mdstyledControl size; data-density scales each size further.

Breadcrumb.List

Extends HTMLOListElement — every native attribute of that element is accepted and forwarded.

No props of its own.

Breadcrumb.Item

Extends HTMLLIElement — every native attribute of that element is accepted and forwarded.

No props of its own.

Extends HTMLAnchorElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultFromDescription
asChildbooleanfalseheadlessRenders the child element instead of a native <a>, merging all of Breadcrumb.Link's props — href, event handlers, className, ref — onto it via Slot. Use for routing-library link components (e.g. React Router's <Link>) that need breadcrumb semantics. See the asChild example on BreadcrumbLink.

Breadcrumb.Page

Extends HTMLSpanElement — every native attribute of that element is accepted and forwarded.

No props of its own.

Breadcrumb.Separator

Extends HTMLLIElement — every native attribute of that element is accepted and forwarded.

No props of its own.

Breadcrumb.Ellipsis

Extends HTMLSpanElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultFromDescription
asChildbooleanfalseheadlessRenders the child element instead of a native <span>, merging all of Breadcrumb.Ellipsis's props — role, aria-hidden, className, ref — onto it via Slot. See the asChild example on BreadcrumbEllipsis.

Styling contract

Control frame

--primitiv-breadcrumb-gap--primitiv-breadcrumb-icon-size--primitiv-breadcrumb-font-family--primitiv-breadcrumb-font-size--primitiv-breadcrumb-font-weight--primitiv-breadcrumb-line-height--primitiv-breadcrumb-link-color--primitiv-breadcrumb-link-color-hover--primitiv-breadcrumb-page-color

Group label & separator

--primitiv-breadcrumb-separator-color

Data attributes

BreadcrumbPage

className: .primitiv-breadcrumb__page

AttributeValueWhen
aria-currentpagealways

Accessibility

  • It's a <nav> landmark. Breadcrumb renders <nav>; if a page has more than one navigation landmark, give it a label (aria-label="Breadcrumb") so a screen reader can tell them apart.
  • The current page is a Breadcrumb.Page, not a link. It is a <span> with aria-current="page", which is how assistive technology announces “you are here”. Making the last crumb a link that points at the current URL is the common mistake — you can't navigate to where you already are.
  • Separators are decorative and hidden. Breadcrumb.Separator is role="presentation" / aria-hidden, so the trail is announced as a clean list of pages without “slash” between each. Whatever glyph you pass stays hidden — no extra ARIA needed.
  • The list is ordered. Breadcrumb.List is an <ol>, because a trail has a meaningful sequence (root → current); assistive technology conveys the order and position from the real list markup.
  • Collapsed crumbs must stay reachable. A static Breadcrumb.Ellipsis hides pages from sight and from the keyboard. If the hidden crumbs need to be navigable, use BreadcrumbOverflow, whose ellipsis opens a real menu, rather than dropping them behind a bare glyph.

Examples

A breadcrumb trail

The trail: a <nav> around an <ol> of items. Every step but the last is a Breadcrumb.Link; the last is a Breadcrumb.Page, which is not a link and carries aria-current="page" — the reader is already there. Put a Breadcrumb.Separator between items; it defaults to / and is aria-hidden, so assistive technology hears “Home, Components, Disclosure, Breadcrumb”, not the slashes.

Density
import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from "@/components/ui/breadcrumb";
<Breadcrumb>  <BreadcrumbList>    <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator />    <BreadcrumbItem><BreadcrumbLink href="/components">Components</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator />    <BreadcrumbItem><BreadcrumbPage>Breadcrumb</BreadcrumbPage></BreadcrumbItem>  </BreadcrumbList></Breadcrumb>

Custom separator

Breadcrumb.Separator defaults to a / glyph; pass children to replace it — a chevron is the common alternative. It stays aria-hidden whatever you put in it, so a decorative icon needs no extra ARIA. Keep it consistent across the whole trail.

Density
import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from "@/components/ui/breadcrumb";import { ChevronRight } from "@primitiv-ui/icons";
<Breadcrumb>  <BreadcrumbList>    <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator><ChevronRight /></BreadcrumbSeparator>    <BreadcrumbItem><BreadcrumbLink href="/components">Components</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator><ChevronRight /></BreadcrumbSeparator>    <BreadcrumbItem><BreadcrumbPage>Breadcrumb</BreadcrumbPage></BreadcrumbItem>  </BreadcrumbList></Breadcrumb>

Collapsing a long trail

A deep trail can collapse its middle behind a Breadcrumb.Ellipsis — a presentational … in place of the hidden crumbs. This one is static: it just shows the ellipsis. For an ellipsis that actually opens a menu of the hidden pages (and decides how many to keep at each end), reach for BreadcrumbOverflow, which composes this with a Dropdown.

Density
import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from "@/components/ui/breadcrumb";
<Breadcrumb>  <BreadcrumbList>    <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>    <BreadcrumbSeparator />    <BreadcrumbItem><BreadcrumbEllipsis /></BreadcrumbItem>    <BreadcrumbSeparator />    <BreadcrumbItem><BreadcrumbPage>Breadcrumb</BreadcrumbPage></BreadcrumbItem>  </BreadcrumbList></Breadcrumb>

Routing links (asChild)

Breadcrumb.Link is a real <a> by default — right for plain hrefs. With a routing library, use asChild to merge the link styling onto your router's own <Link>, so navigation stays client-side without a full reload while the crumb still reads as a link. The current page stays a Breadcrumb.Page, never a router link.

Density
import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from "@/components/ui/breadcrumb";import Link from "next/link";
<BreadcrumbItem>  <BreadcrumbLink asChild>    <Link href="/components">Components</Link>  </BreadcrumbLink></BreadcrumbItem>

Sizes and density

Five sizes, each rescaling again with the nearest data-density ancestor. A breadcrumb usually wants to be quiet — xs/sm under a page title — so the smaller end of the ramp is the common choice.

Density
import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis } from "@/components/ui/breadcrumb";
<div data-density="comfortable">  <Breadcrumb>    <BreadcrumbList>      <BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>      <BreadcrumbSeparator />      <BreadcrumbItem><BreadcrumbLink href="/components">Components</BreadcrumbLink></BreadcrumbItem>      <BreadcrumbSeparator />      <BreadcrumbItem><BreadcrumbPage>Breadcrumb</BreadcrumbPage></BreadcrumbItem>    </BreadcrumbList>  </Breadcrumb></div>