Skip to content

AppBreadcrumb

Hero example

When to use

Reach for AppBreadcrumb at the top of every dashboard page that sits inside the hierarchical navigation — entities/ent_… › Filings › Delaware AR 2026. The trail tells the user where they are and how to back out one level. The component handles three crumb shapes: plain (no href or onClick), interactive link (href set), interactive button (onClick set). The last crumb is always the current page and renders as a non-interactive <span> with aria-current="page".

Don't reach for AppBreadcrumb for non-hierarchical navigation — the trail implies "you got here by descending through these ancestors." For lateral tabs use SlidingPill; for cross-domain pivots use a route-grammar nav primitive. Don't use AppBreadcrumb on the marketing site either — the dashboard hierarchy doesn't exist there.

Do — render AppBreadcrumb at the top of every dashboard detail page. The trail anchors the user's mental model.
Don't — render AppBreadcrumb on a top-level page (no ancestors). One crumb is just a page title.

Anatomy

Three structural parts:

  1. <nav aria-label="Breadcrumb">. The outer element carries the breadcrumb landmark role. Screen readers can jump to it via landmark navigation.
  2. <ol class="m-crumbs__list">. Each crumb is an <li> containing either a <span aria-current="page"> (last crumb), an <a> (with href), a <button> (with onClick only), or a plain <span> (no interactivity). A CrumbSep SVG sits between consecutive crumbs.
  3. Right slot. Optional. Renders at the trailing edge with margin-left: auto — typical use is a context-menu trigger ("⋯") or a small action button ("Edit").

Mono crumbs (mono: true) render in Geist Mono at 11.5px — for resource IDs, file paths, and other code-like identifiers.

Props

PropTypeDefaultDescription
crumbsrequiredCrumb[]
rightReact.ReactNode

States

StateTriggerVisual
Default crumbCrumb with no interactivityMuted text (--fg-muted)
Linked crumbhref setSame color; hover brightens to --fg; focus ring on Tab
Button crumbonClick setSame as linked but renders as <button>
Current crumbLast in crumbs[]Foreground --fg, weight 500, aria-current="page"
Emptycrumbs: []Returns null — nothing renders

Density

In compact, the gap between crumbs drops from 6 to 4px. Font sizes and separator dimensions stay constant.

Themes

Tokens consumed

colors6 tokensv2.3.0
#0D0D0D
matter
src ↗
#5A5A5A
matter
src ↗
#8C8C8C
matter
src ↗
#707070
matter
src ↗
#14110D
matter
src ↗
#627e68
brand
src ↗

Current crumb reads --fg; earlier crumbs read --fg-muted. Separator reads --hairline-strong.

Accessibility

  • Keyboard interactions. Interactive crumbs (link or button) are reachable via Tab. Enter or Space activates. Non-interactive crumbs (plain or current) are skipped.
  • ARIA roles and properties. Outer <nav aria-label="Breadcrumb">. List is <ol>. Last crumb carries aria-current="page". Separator SVG carries aria-hidden.
  • Focus order. DOM order — earliest crumb first, current crumb last (not focusable).
  • Screen-reader expectations. Announces as "Breadcrumb, list, {N} items: {crumb 1}, link; {crumb 2}, link; …; {current crumb}, current page."
  • Reduced motion. No motion to suppress.
  • Forced colors. Separators → ButtonBorder; current → HighlightText; earlier → ButtonText.
  • WIG rules. <nav> for the landmark (semantic-element rule). aria-current="page" on the last crumb (WIG navigation-state rule). Link vs button choice depends on whether the crumb navigates to a different page (<a>) or triggers an in-page action (<button>). :focus-visible ring on interactive crumbs.

Do / Don't

Do — pass href for crumbs that navigate to a route. Right-click "open in new tab" works as expected (WIG link-vs-button rule).
Don't — use onClick to navigate. The middle-click / cmd-click affordance breaks.
Do — pass mono: true for crumbs that show resource IDs (ent_a1b2c3) or file paths. The mono treatment signals "code-like identity."
Don't — render more than ~5 crumbs. Deep trails read as poor architecture; collapse the middle with an ellipsis crumb.
Do — use the right slot for a small action affordance — context menu, edit button, follow toggle.
Don't — pack the right slot with a multi-action toolbar. The trail's identity is "where am I," not "what can I do here."

Recipes

This component appears in:

Code example

import { AppBreadcrumb } from "@matter/components";

export function EntityDetailHeader({ entity, filing }: { entity: Entity; filing: Filing }) {
  return (
    <AppBreadcrumb
      crumbs={[
        { label: "Entities", href: "/entities" },
        { label: entity.legal_name, href: `/entities/${entity.id}` },
        { label: entity.id, href: `/entities/${entity.id}`, mono: true },
        { label: "Filings", href: `/entities/${entity.id}/filings` },
        { label: filing.title },
      ]}
      right={<ContextMenuTrigger />}
    />
  );
}

Source

packages/components/src/AppBreadcrumb/AppBreadcrumb

On this page