Skip to content
WANCRAFT

D.105Navigation & Menus

Smart Breadcrumbs

Breadcrumbs that measure themselves and fold middle items into an accessible overflow menu when space runs short.

NewReactMotionTailwindintermediate

Overview

Smart Breadcrumbs keeps a long trail on one line at any width. An invisible ruler measures every crumb, and a ResizeObserver recalculates how many middle items have to fold away whenever the container or the fonts change. The first items and the last few (including the current page) always stay visible; the folded ones move into a "…" menu button with full menu keyboard support. Crumbs slide out and back in on a layout spring as space shrinks and grows, the current page carries aria-current, separators are hidden from assistive technology and every item can have an optional icon.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/smart-breadcrumbs.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/smart-breadcrumbs.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/smart-breadcrumbs.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { SmartBreadcrumbs, type BreadcrumbItem } from "@/components/ui/smart-breadcrumbs";

export function SmartBreadcrumbsExample() {
  const trail: BreadcrumbItem[] = [
    { label: "Home", href: "/" },
    { label: "Workspace", href: "/workspace" },
    { label: "Projects", href: "/workspace/projects" },
    { label: "Atlas Rebrand", href: "/workspace/projects/atlas" },
    { label: "Design", href: "/workspace/projects/atlas/design" },
    { label: "Typography scale" },
  ];

  return (
    <SmartBreadcrumbs items={trail} />
  );
}

03Props

PropTypeDefaultDescription
itemsBreadcrumbItem[]—Required. { id?, label, href?, icon? } from the root to the current page (last item).
keepFirstnumber1Items at the start that never fold.
keepLastnumber2Items at the end that never fold, including the current page (min 1).
separator"chevron" | "slash" | "dot""chevron"Separator glyph (aria-hidden).
size"sm" | "md""md"Type and icon size.
spring{ stiffness; damping; mass }{ 420, 36, 0.8 }Spring for the collapse / expand layout animation.
accentstring"#FF7448"Hover underline and focus ring colour.
labelstring"Breadcrumb"Accessible name of the nav landmark.
overflowLabelstring"Show hidden pages"Accessible name of the overflow button (the hidden count is appended).
onNavigate(item, event) => void—Called when a crumb or menu item is clicked, before navigation (call event.preventDefault() for client routing).
...propsHTMLAttributes<HTMLElement>—Forwarded to the <nav>.

04Features

  • 01Invisible ruler + ResizeObserver: folds exactly as many middle items as needed, re-measures on resize and font load
  • 02Keeps the first N and last N items; folds at least two items so “…” never replaces a single crumb
  • 03Overflow menu button lists the folded pages in order, with their position in the trail
  • 04Crumbs slide out and back in on a layout spring as space changes
  • 05When even the folded trail does not fit, ancestors shrink to their icon (label kept for screen readers and as a tooltip) and the current page truncates
  • 06Optional icons, three separator styles, two sizes
motion · layoutmotion · springinput · clickinput · keyboardinput · viewport

05Accessibility

  • A <nav> landmark ("Breadcrumb") with an ordered list; the current page has aria-current="page".
  • Separators are aria-hidden; the measuring ruler is aria-hidden and invisible.
  • The overflow button has aria-haspopup="menu", aria-expanded, aria-controls and a label with the hidden count.
  • Menu keyboard support: Enter / Space / ArrowDown opens on the first item, ArrowUp on the last; arrows, Home and End move; Escape closes and returns focus; Tab closes.
  • Visible focus rings on every crumb, the overflow button and menu items.
  • With prefers-reduced-motion, collapsing and the menu happen without movement.

06Limitations

  • Ancestor labels longer than 12rem truncate visually; their full label stays in the accessible name.
  • The nav fills its parent's width; inside a shrink-to-fit parent give it an explicit width.