Skip to content
Drivn
6 min read

React Breadcrumb Examples — Separators, Links, Ellipsis

Copy-paste React Breadcrumb patterns — default chevron, custom separator, Next.js Link integration, collapsed ellipsis, and dynamic pathname rendering.

A breadcrumb answers two questions at once: where am I, and how do I get back up one level? That is why the pattern survives in documentation sites, e-commerce catalogs, file browsers, settings trees, and any admin panel deeper than two levels. It looks simple enough to hand-roll, and most teams do — then rediscover the details one bug at a time: semantic list markup, separator consistency, aria-current on the final segment, graceful wrapping on a narrow phone.

Drivn's Breadcrumb settles those details in one file with zero runtime UI dependencies. The root renders a <nav aria-label="Breadcrumb"> wrapping an <ol>, and separators are injected between siblings during render, so you write items rather than dividers. Every piece hangs off the root through dot notation: Breadcrumb.Item for a link, Breadcrumb.Page for the current segment, Breadcrumb.Separator when you want to place one by hand, and Breadcrumb.Ellipsis for a collapsed middle.

Below are five patterns worth copying: the default chevron trail for a fixed-depth docs page, a slash-separated trail for settings hierarchies, a next/link swap that gives every breadcrumb client-side routing, an ellipsis that keeps deep paths on one line, and a trail computed from the live pathname. Install first via the CLI; for the engineering tradeoffs against shadcn/ui, read Drivn vs shadcn/ui Breadcrumb.

Default chevron separator

The default breadcrumb uses a ChevronRight icon between every item and renders the final segment as a non-interactive <Breadcrumb.Page>. You write only the items and the current page — separators are injected automatically between siblings at render time, so a three-level breadcrumb has three JSX children, not five.

The root applies flex items-center gap-1.5 flex-wrap text-sm text-muted-foreground so the trail wraps gracefully on narrow viewports. Each Breadcrumb.Item becomes a muted link that darkens on hover via hover:text-foreground; the current page gets font-medium text-foreground so it reads as the anchor of the trail. Use this pattern anywhere you have a clean, fixed-depth hierarchy — docs pages, admin sections, or content categories.

1import { Breadcrumb } from '@/components/ui/breadcrumb'
2
3<Breadcrumb>
4 <Breadcrumb.Item href="/">Home</Breadcrumb.Item>
5 <Breadcrumb.Item href="/docs">Docs</Breadcrumb.Item>
6 <Breadcrumb.Page>Button</Breadcrumb.Page>
7</Breadcrumb>

Custom slash separator

Passing a separator prop to the root swaps the chevron for any JSX node — a slash character, a dot, a vertical bar, or an icon from a different set. The auto-injector uses whatever you pass once, so every position in the trail stays visually consistent without repeating the separator at each item.

Slashes suit file-path-style breadcrumbs where the trail represents a literal hierarchy: user settings, file tree navigation, storage keys. Dots work well for compact mobile layouts. For a branded look, pass a styled <span> that references design tokens so the separator picks up your theme's muted color automatically. The separator prop is typed as React.ReactNode, so there is no escape hatch needed for custom markup — any valid JSX works and inherits your Tailwind classes.

1<Breadcrumb separator={<span>/</span>}>
2 <Breadcrumb.Item href="/">Home</Breadcrumb.Item>
3 <Breadcrumb.Item href="/settings">Settings</Breadcrumb.Item>
4 <Breadcrumb.Page>Profile</Breadcrumb.Page>
5</Breadcrumb>

Collapsed path with Ellipsis

Eight folders down, or a product buried in nested categories, and the trail either wraps onto a second line or pushes past the edge of its card. Breadcrumb.Ellipsis collapses the middle instead: keep the root, keep the current page, and replace everything between them with one glyph that signals there are levels here.

The component renders an <li> holding a size-9 span — a 36-pixel square, matching the tap target of an icon button — with a MoreHorizontal icon at size-4 and an sr-only "More pages" label, so assistive tech announces the gap instead of skipping past it. Drop it anywhere in the children list and the root's auto-injection places chevrons on both sides, exactly as it does for any other item.

It is non-interactive out of the box, which is the right default — a decorative collapse should not advertise a click that does nothing. When you do want the hidden levels reachable, wrap the ellipsis in a Popover or a dropdown at the call site and list the skipped segments inside. The Breadcrumb file itself needs no change.

1<Breadcrumb>
2 <Breadcrumb.Item href="/">Home</Breadcrumb.Item>
3 <Breadcrumb.Ellipsis />
4 <Breadcrumb.Item href="/docs/components">
5 Components
6 </Breadcrumb.Item>
7 <Breadcrumb.Page>Button</Breadcrumb.Page>
8</Breadcrumb>

Render dynamically from route segments

Static breadcrumbs work for fixed-depth pages, but most apps compute the trail from the current URL. The pattern is a small helper that splits pathname into segments, accumulates an href for each prefix, maps the segment to a human-readable label, and renders the array as a list of Breadcrumb.Item elements with the final entry as a Breadcrumb.Page.

For a Next.js App Router app, call usePathname() inside a client component, split the path on /, and filter out empty strings. Pair it with a small label map — { docs: "Docs", components: "Components" } — so URL slugs render as proper titles in the trail. This pattern scales to any depth without manual edits: new routes show up with correctly-formatted breadcrumbs automatically, and the final segment is always the current page. Drop the component into your app shell once and every page inherits the trail.

1'use client'
2import { usePathname } from 'next/navigation'
3import { Breadcrumb } from '@/components/ui/breadcrumb'
4
5const labels: Record<string, string> = {
6 docs: 'Docs',
7 components: 'Components',
8 examples: 'Examples',
9}
10
11export function DynamicBreadcrumb() {
12 const pathname = usePathname()
13 const segments = pathname.split('/').filter(Boolean)
14
15 return (
16 <Breadcrumb>
17 <Breadcrumb.Item href="/">Home</Breadcrumb.Item>
18 {segments.map((segment, i) => {
19 const href = '/' + segments.slice(0, i + 1).join('/')
20 const label = labels[segment] ?? segment
21 const isLast = i === segments.length - 1
22
23 return isLast ? (
24 <Breadcrumb.Page key={href}>{label}</Breadcrumb.Page>
25 ) : (
26 <Breadcrumb.Item key={href} href={href}>
27 {label}
28 </Breadcrumb.Item>
29 )
30 })}
31 </Breadcrumb>
32 )
33}
Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

They are auto-injected at render time. The root calls React.Children.toArray(children).flatMap() and, for every child past the first, prepends a Breadcrumb.Separator element before it. The separator uses whatever JSX you pass via the root's separator prop, defaulting to a Lucide ChevronRight icon. You write only the items and the current page inside <Breadcrumb>, and the trail renders with visually consistent dividers without any per-position separator tags in your JSX.

Pass a separator prop to the root component once. The prop is typed as React.ReactNode, so any JSX works: a slash character (<span>/</span>), a custom icon (<ArrowRight className="w-3 h-3" />), or a branded element styled with your design tokens. The root hands that node to every auto-injected separator in the list, so a single override applies across every item position. To change the default globally, set the prop in a layout-level wrapper so every Breadcrumb inherits the same glyph.

Edit the local breadcrumb.tsx after install. Open src/components/ui/breadcrumb.tsx, import Link from next/link, and replace the <a> tag inside the Item function with a <Link>. The href prop and the {...props} spread transfer cleanly because LinkProps extends the same anchor attribute type. Every breadcrumb in your app then uses client-side routing with no call-site changes. This is the canonical "copy and own" pattern — Drivn never ships wrapper props for framework integration.

Use it when the trail depth exceeds the available horizontal space and wrapping onto two lines hurts more than collapsing the middle. Typical thresholds are four or more levels in a card-constrained layout, or any breadcrumb rendering inside a narrow sidebar or mobile viewport. Place the ellipsis between the root item and the deepest visible item, so users still see where they are and the top-level context. If you want the hidden levels to be clickable, wrap the ellipsis in a disclosure component like a Popover.

Call usePathname() from next/navigation inside a client component, split the pathname on /, filter empty strings, and map each segment to a Breadcrumb.Item with an accumulated href. Render the last segment as a Breadcrumb.Page so it picks up the current-page styling and the aria-current="page" marker. Keep a labels record that maps URL slugs to human-readable titles so the trail reads as "Docs / Components" instead of "docs / components". The pattern scales to any route depth without manual edits.