Loading…
Loading…
Shows the user's location within a site hierarchy and enables upward navigation.
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:
When NOT to use a Breadcrumb:
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.
| Variant | Description | Use Case |
|---|---|---|
| Location-based | Shows the user's position in the site hierarchy. Each crumb maps to a structural level. | Most common. E-commerce, docs, admin panels. |
| Attribute-based | Displays attributes or filters the user has applied to arrive at the current view. | Faceted search results (e.g., Shoes → Size 10 → Black). |
| Path-based | Shows the literal navigation path the user took, not the structural hierarchy. | Rarely recommended — duplicates the browser's history. |
| Collapsed / Truncated | Middle levels collapse into an ellipsis menu (…) to save space. | Deep hierarchies (5+ levels), responsive layouts. |
| Separator | Character | Feel |
|---|---|---|
| Chevron | › or SVG chevron-right | Modern, directional. Most popular. |
| Slash | / | Developer-oriented, file-path aesthetic. |
| Arrow | → | Explicit directionality. |
| Custom icon | Any SVG | Brand-specific. Keep it small (12–16px). |
| Size | Font Size | Use Case |
|---|---|---|
| Small | 12–13px | Dense admin UIs, toolbars |
| Medium | 14px | Default — general-purpose pages |
| Large | 16px | Marketing pages, hero subheaders |
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.
| Property | Type | Default | Description |
|---|---|---|---|
items | BreadcrumbItem[] | — | Array of crumb objects with label and optional href. The last item is the current page. |
separator | ReactNode | string | '›' | Visual delimiter rendered between crumbs. Can be a string or custom icon component. |
maxItems | number | undefined | When set, collapses intermediate crumbs into an ellipsis dropdown beyond this count. |
itemsBeforeCollapse | number | 1 | Number of leading items to keep visible when collapsing. |
itemsAfterCollapse | number | 1 | Number of trailing items to keep visible when collapsing. |
className | string | — | CSS class applied to the <nav> wrapper. |
aria-label | string | 'Breadcrumb' | Accessible name for the navigation landmark. |
| Property | Type | Description |
|---|---|---|
label | string | Display text for the crumb |
href | string | undefined | URL for the crumb link. Undefined for the current (last) page. |
icon | ReactNode | Optional leading icon (e.g., home icon for the root crumb) |
| Token | Role | Typical Value |
|---|---|---|
--breadcrumb-font-size | Base text size | 0.875rem (14px) |
--breadcrumb-font-weight | Link weight | 400 |
--breadcrumb-font-weight-current | Current page weight | 600 |
--breadcrumb-color-link | Interactive crumb color | var(--color-text-secondary) |
--breadcrumb-color-link-hover | Hover state | var(--color-text-primary) |
--breadcrumb-color-current | Current (non-interactive) crumb | var(--color-text-primary) |
--breadcrumb-color-separator | Separator glyph color | var(--color-text-tertiary) |
--breadcrumb-gap | Space between crumbs and separators | 0.5rem (8px) |
--breadcrumb-separator-size | Size of separator icon | 0.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.
| State | Visual Treatment | Behavior |
|---|---|---|
| Default | Crumb links rendered in secondary text color, current page in primary/bold. | All ancestor crumbs are clickable. |
| Hover | Link text color shifts to primary; optional underline appears. | Cursor changes to pointer. |
| Focus | Visible focus ring (2px outline) on the focused crumb link. | Must be clearly visible per WCAG 2.4.7. |
| Active / Pressed | Subtle color darkening or underline. | Provides click confirmation. |
| Current page | Rendered as plain text (not a link). Uses aria-current="page". | Not clickable — the user is already here. |
| Collapsed | Middle crumbs replaced by an ellipsis button (…). | Clicking the ellipsis reveals hidden crumbs in a dropdown. |
| Overflow / Truncated | Long 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.
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:
<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.<ol> (ordered list) inside the <nav>. The ordered list conveys sequence and hierarchy to assistive technology.aria-current="page" on the last link or span (WCAG 1.3.1). Screen readers announce "current page" alongside the label.::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:
Tab, Enter) applies (WCAG 2.1.1 Keyboard).Color & Contrast:
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.
Do:
<h1> or navigation label<ol> for semantic ordering — the hierarchy mattersBreadcrumbList JSON-LD) for SEO rich resultsDon't:
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.
<!-- 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>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}
/>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.