Skip to content

Breadcrumb

Shows the user's location within a site hierarchy and enables upward navigation.

  • Breadcrumb Trail
  • Path Navigation

Overview

The Breadcrumb is a secondary navigation pattern that reveals the user's current location within a site's hierarchical structure. It renders a horizontal trail of links — each representing a level in the information architecture — separated by visual delimiters, allowing single-click access to any ancestor page.

Breadcrumbs solve a fundamental wayfinding problem: "Where am I, and how do I get back?" Unlike a browser's back button, which traces temporal history, breadcrumbs expose structural hierarchy. This distinction is critical for content-heavy websites — e-commerce stores, documentation portals, knowledge bases — where a user may arrive on a deep page via search and have no temporal navigation history at all.

When to use a Breadcrumb:

  • Sites with three or more levels of hierarchy
  • E-commerce product pages (Home → Category → Subcategory → Product)
  • Documentation and knowledge-base portals
  • Admin dashboards with nested settings panels
  • Any page where the user might arrive via search and need orientation

When NOT to use a Breadcrumb:

  • Single-level or flat site structures — breadcrumbs add noise without value
  • Linear wizard flows — use a Stepper instead
  • Mobile-first apps with tab-based navigation — breadcrumbs rarely translate well to small screens
  • When the hierarchy is ambiguous (a page belongs to multiple categories) — present only the canonical path

Breadcrumbs also deliver significant SEO benefits. Google surfaces breadcrumb trails as rich results in search listings, improving click-through rates. When paired with BreadcrumbList structured data (JSON-LD), they provide search engines explicit hierarchy signals. Validate your breadcrumb contrast ratios with the Contrast Checker to ensure link legibility at every level.

Variants

Breadcrumb Variants

VariantDescriptionUse Case
Location-basedShows the user's position in the site hierarchy. Each crumb maps to a structural level.Most common. E-commerce, docs, admin panels.
Attribute-basedDisplays attributes or filters the user has applied to arrive at the current view.Faceted search results (e.g., Shoes → Size 10 → Black).
Path-basedShows the literal navigation path the user took, not the structural hierarchy.Rarely recommended — duplicates the browser's history.
Collapsed / TruncatedMiddle levels collapse into an ellipsis menu (…) to save space.Deep hierarchies (5+ levels), responsive layouts.

Separator Variants

SeparatorCharacterFeel
Chevron› or SVG chevron-rightModern, directional. Most popular.
Slash/Developer-oriented, file-path aesthetic.
Arrow→Explicit directionality.
Custom iconAny SVGBrand-specific. Keep it small (12–16px).

Size Variants

SizeFont SizeUse Case
Small12–13pxDense admin UIs, toolbars
Medium14pxDefault — general-purpose pages
Large16pxMarketing pages, hero subheaders

Responsive Behavior

On small viewports, breadcrumbs should either collapse to show only the immediate parent ("← Back to Category") or truncate intermediate levels into an expandable ellipsis dropdown. Full breadcrumb trails on mobile waste precious vertical space and rarely provide enough tap-target area per crumb.

Properties

Breadcrumb Properties

PropertyTypeDefaultDescription
itemsBreadcrumbItem[]—Array of crumb objects with label and optional href. The last item is the current page.
separatorReactNode | string'›'Visual delimiter rendered between crumbs. Can be a string or custom icon component.
maxItemsnumberundefinedWhen set, collapses intermediate crumbs into an ellipsis dropdown beyond this count.
itemsBeforeCollapsenumber1Number of leading items to keep visible when collapsing.
itemsAfterCollapsenumber1Number of trailing items to keep visible when collapsing.
classNamestring—CSS class applied to the <nav> wrapper.
aria-labelstring'Breadcrumb'Accessible name for the navigation landmark.

BreadcrumbItem Shape

PropertyTypeDescription
labelstringDisplay text for the crumb
hrefstring | undefinedURL for the crumb link. Undefined for the current (last) page.
iconReactNodeOptional leading icon (e.g., home icon for the root crumb)

Token Mappings

Design Token Mappings

TokenRoleTypical Value
--breadcrumb-font-sizeBase text size0.875rem (14px)
--breadcrumb-font-weightLink weight400
--breadcrumb-font-weight-currentCurrent page weight600
--breadcrumb-color-linkInteractive crumb colorvar(--color-text-secondary)
--breadcrumb-color-link-hoverHover statevar(--color-text-primary)
--breadcrumb-color-currentCurrent (non-interactive) crumbvar(--color-text-primary)
--breadcrumb-color-separatorSeparator glyph colorvar(--color-text-tertiary)
--breadcrumb-gapSpace between crumbs and separators0.5rem (8px)
--breadcrumb-separator-sizeSize of separator icon0.75rem

Map these tokens to your global palette. Use the Contrast Checker to verify that --breadcrumb-color-link achieves at least 4.5:1 against your background for WCAG AA compliance.

States

Breadcrumb States

StateVisual TreatmentBehavior
DefaultCrumb links rendered in secondary text color, current page in primary/bold.All ancestor crumbs are clickable.
HoverLink text color shifts to primary; optional underline appears.Cursor changes to pointer.
FocusVisible focus ring (2px outline) on the focused crumb link.Must be clearly visible per WCAG 2.4.7.
Active / PressedSubtle color darkening or underline.Provides click confirmation.
Current pageRendered as plain text (not a link). Uses aria-current="page".Not clickable — the user is already here.
CollapsedMiddle crumbs replaced by an ellipsis button (…).Clicking the ellipsis reveals hidden crumbs in a dropdown.
Overflow / TruncatedLong labels truncated with text-overflow: ellipsis. Max-width per crumb.Full label visible on hover via native title or a Tooltip.

The current-page crumb must never be an interactive link. Rendering it as a <span> with aria-current="page" is semantically correct and avoids the confusion of a link that navigates to the page the user is already on.

Accessibility

Accessibility

Breadcrumbs have an excellent accessibility story when implemented correctly — they are one of the few components where the WAI-ARIA Authoring Practices provide a near-complete recipe.

ARIA Requirements:

  • Wrap the breadcrumb in a <nav> element with aria-label="Breadcrumb" (WCAG 1.3.1 Info and Relationships). This creates a navigation landmark that screen-reader users can jump to directly.
  • Render the crumb list as an <ol> (ordered list) inside the <nav>. The ordered list conveys sequence and hierarchy to assistive technology.
  • Mark the current page with aria-current="page" on the last link or span (WCAG 1.3.1). Screen readers announce "current page" alongside the label.
  • Separators must be decorative — either rendered via CSS ::before/::after pseudo-elements or marked with aria-hidden="true". If separators are exposed to the accessibility tree, screen readers will announce every chevron, creating a painful experience.

Keyboard Navigation:

  • Each crumb link is part of the natural tab order. No special key handling is required — standard link behavior (Tab, Enter) applies (WCAG 2.1.1 Keyboard).
  • The current page crumb, rendered as a non-interactive element, is skipped during tab navigation — correct and expected.

Color & Contrast:

  • Link text must maintain at least 4.5:1 contrast ratio against the background (WCAG 1.4.3 Contrast Minimum). Verify using the Contrast Checker.
  • Non-underlined links must be distinguishable from surrounding non-link text by means other than color alone (WCAG 1.4.1 Use of Color). Options: underline on hover/focus, different font weight, or an icon.
  • Focus indicators must have at least 3:1 contrast against adjacent colors (WCAG 2.4.11 Focus Appearance, Level AAA; 2.4.7 Focus Visible, Level AA).

Screen Reader Behavior: A properly marked-up breadcrumb announces as: "Breadcrumb, navigation" → "list, 4 items" → "link, Home" → "link, Products" → "link, Shoes" → "current page, Running Shoes". This gives full hierarchical context without separator noise.

Structured Data for SEO: Add BreadcrumbList JSON-LD alongside the visible breadcrumb. This is not an accessibility requirement but it helps search engines understand your site's hierarchy and display breadcrumb trails in search results.

Usage Guidelines

Usage Guidelines

Do:

  • Start with the site root (e.g., "Home") as the first crumb
  • Use short, descriptive labels that match the page's <h1> or navigation label
  • Place breadcrumbs above the page title, below the primary navigation bar
  • Use <ol> for semantic ordering — the hierarchy matters
  • Include structured data (BreadcrumbList JSON-LD) for SEO rich results
  • Collapse intermediate levels with an ellipsis when depth exceeds 4–5 levels
  • Show only the canonical path when a page exists in multiple categories

Don't:

  • Don't make the current page a link — it's where the user already is
  • Don't use breadcrumbs as the sole navigation mechanism — they supplement, not replace, primary navigation
  • Don't include the current page's parent twice (once in breadcrumb, once as a "Back" link)
  • Don't use breadcrumbs in mobile apps that have tab bars — the paradigms conflict
  • Don't expose separators to the accessibility tree
  • Don't truncate labels so aggressively that they become meaningless (e.g., "Pro…" for "Products")

Placement: The canonical position is between the global navigation bar and the page heading. This matches the top-down mental model: global nav → breadcrumb trail → page content. Some systems place breadcrumbs inside a page header or toolbar — acceptable as long as the landmark is still discoverable.

SEO Considerations: Google explicitly supports BreadcrumbList structured data. Adding it improves how your pages appear in search results — breadcrumb trails replace raw URLs, increasing click-through rates by 20–30% in some studies. Ensure the structured data hierarchy matches the visible breadcrumb exactly.

Code Snippets

html
<!-- Breadcrumb – Semantic HTML -->
<nav aria-label="Breadcrumb">
  <ol class="breadcrumb">
    <li class="breadcrumb-item">
      <a href="/">Home</a>
    </li>
    <li class="breadcrumb-item">
      <a href="/products">Products</a>
    </li>
    <li class="breadcrumb-item">
      <a href="/products/shoes">Shoes</a>
    </li>
    <li class="breadcrumb-item">
      <span aria-current="page">Running Shoes</span>
    </li>
  </ol>
</nav>

<style>
.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: var(--breadcrumb-font-size, 0.875rem);
}

.breadcrumb-item + .breadcrumb-item::before {
  content: "›";
  color: var(--breadcrumb-color-separator, #9ca3af);
  margin-right: 0.5rem;
}

.breadcrumb-item a {
  color: var(--breadcrumb-color-link, #6b7280);
  text-decoration: none;
}

.breadcrumb-item a:hover {
  color: var(--breadcrumb-color-link-hover, #111827);
  text-decoration: underline;
}

.breadcrumb-item [aria-current="page"] {
  color: var(--breadcrumb-color-current, #111827);
  font-weight: 600;
}
</style>

<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
    { "@type": "ListItem", "position": 2, "name": "Products", "item": "https://example.com/products" },
    { "@type": "ListItem", "position": 3, "name": "Shoes", "item": "https://example.com/products/shoes" },
    { "@type": "ListItem", "position": 4, "name": "Running Shoes" }
  ]
}
</script>
tsx
interface BreadcrumbItem {
  label: string;
  href?: string;
  icon?: React.ReactNode;
}

interface BreadcrumbProps {
  items: BreadcrumbItem[];
  separator?: React.ReactNode;
  maxItems?: number;
  className?: string;
}

function Breadcrumb({ items, separator = "›", maxItems, className }: BreadcrumbProps) {
  const [expanded, setExpanded] = React.useState(false);

  const visibleItems = React.useMemo(() => {
    if (!maxItems || expanded || items.length <= maxItems) return items;
    return [
      items[0],
      { label: "…", href: undefined } as BreadcrumbItem,
      ...items.slice(-(maxItems - 1)),
    ];
  }, [items, maxItems, expanded]);

  return (
    <nav aria-label="Breadcrumb" className={className}>
      <ol style={{ display: "flex", alignItems: "center", gap: "0.5rem", listStyle: "none", padding: 0, margin: 0 }}>
        {visibleItems.map((item, index) => (
          <li key={index} style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
            {index > 0 && (
              <span aria-hidden="true" style={{ color: "var(--breadcrumb-color-separator, #9ca3af)" }}>
                {separator}
              </span>
            )}
            {item.label === "…" ? (
              <button
                onClick={() => setExpanded(true)}
                aria-label="Show full breadcrumb trail"
                style={{ background: "none", border: "none", cursor: "pointer", fontSize: "inherit", color: "var(--breadcrumb-color-link, #6b7280)" }}
              >
                …
              </button>
            ) : index === visibleItems.length - 1 ? (
              <span aria-current="page" style={{ fontWeight: 600, color: "var(--breadcrumb-color-current, #111827)" }}>
                {item.icon} {item.label}
              </span>
            ) : (
              <a href={item.href} style={{ color: "var(--breadcrumb-color-link, #6b7280)", textDecoration: "none" }}>
                {item.icon} {item.label}
              </a>
            )}
          </li>
        ))}
      </ol>
    </nav>
  );
}

// Usage
<Breadcrumb
  items={[
    { label: "Home", href: "/" },
    { label: "Products", href: "/products" },
    { label: "Shoes", href: "/products/shoes" },
    { label: "Running Shoes" },
  ]}
  separator="›"
  maxItems={4}
/>

Design Systems

Design System Implementations

Material Design (MUI) provides a <Breadcrumbs> component that renders children inside an <ol> wrapped in a <nav aria-label="breadcrumb">. It supports a maxItems prop with itemsBeforeCollapse and itemsAfterCollapse for automatic truncation. The separator defaults to / and is fully customizable. MUI correctly applies aria-current="page" to the last item and hides separators from assistive technology. The collapsed state renders an <IconButton> with an ellipsis icon.

Ant Design implements breadcrumbs with an <Breadcrumb> component accepting an items array of { title, href } objects. It supports a custom separator prop and renders a semantic <nav> with <ol>. Ant also provides a <Breadcrumb.Separator> sub-component for custom delimiters. Its breadcrumb integrates tightly with Ant's routing utilities, automatically generating crumbs from route configuration.

Chakra UI offers a composable <Breadcrumb> with <BreadcrumbItem> and <BreadcrumbLink> sub-components. The isCurrentPage prop on an item renders the link as a <span> with aria-current="page". Separator customization is straightforward via the separator prop. Chakra's breadcrumb inherits from Box, supporting all style props for easy theming.

Bootstrap styles breadcrumbs with its .breadcrumb class on an <ol> inside a <nav>. Separators are added via CSS ::before pseudo-elements, keeping them out of the DOM and accessibility tree by default. The active item uses .active class and aria-current="page". Bootstrap's approach is CSS-first, requiring minimal JavaScript.

Apple Human Interface Guidelines (HIG) does not feature a breadcrumb component — macOS and iOS rely on back buttons and hierarchical navigation controllers instead. Breadcrumbs are primarily a web pattern.

Shadcn/ui provides a composable breadcrumb built on Radix primitives: <Breadcrumb>, <BreadcrumbList>, <BreadcrumbItem>, <BreadcrumbLink>, <BreadcrumbSeparator>, and <BreadcrumbEllipsis>. It uses semantic HTML (<nav>, <ol>) and includes proper aria-current handling. The ellipsis component renders a dropdown menu for collapsed items.

NavigationBreadcrumb TrailPath Navigation