Breadcrumbs
Breadcrumbs say where a page sits. The last item is the current page: it is rendered as plain text and marked aria-current="page", because a link to the page you are already on is noise.
Basic breadcrumbs
breadcrumbs({
items: [
{ label: 'Home', href: '/' },
{ label: 'Docs', href: '/docs' },
{ label: 'Routing' },
],
})Separator
Any string or markup. The separators are hidden from screen readers either way.
stack({ gap: 'md' },
breadcrumbs({
separator: '/',
items: [{ label: 'Home', href: '/' }, { label: 'UI', href: '/ui' }, { label: 'Breadcrumbs' }],
}),
breadcrumbs({
separator: '›',
items: [{ label: 'Home', href: '/' }, { label: 'UI', href: '/ui' }, { label: 'Breadcrumbs' }],
}),
breadcrumbs({
separator: '·',
items: [{ label: 'Home', href: '/' }, { label: 'UI', href: '/ui' }, { label: 'Breadcrumbs' }],
}),
)Plain strings
An item with no href is just text, wherever it appears — not only at the end.
breadcrumbs({
items: ['Home', 'Archive', '2026', 'March'],
})From a path
On a static site the trail is usually derived from the route, not hand-written.
return (() => {
const path = '/docs/guides/routing'
const segments = path.split('/').filter(Boolean)
return breadcrumbs({
items: [
{ label: 'Home', href: '/' },
...segments.map((segment, index) => ({
label: segment.replace(/-/g, ' '),
href: index === segments.length - 1 ? undefined : '/' + segments.slice(0, index + 1).join('/'),
})),
],
})
})()Labelling the nav
The whole thing is a <nav> with an accessible name, so a screen reader can jump to it. Change the name with label when a page has more than one navigation landmark.
breadcrumbs({
label: 'Documentation breadcrumb',
items: [{ label: 'Docs', href: '/docs' }, { label: 'Components' }],
})Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | Array | [] | Strings, or { label, href } objects. The last one is the current page. |
separator | Child | '/' | Drawn between items, hidden from screen readers. |
label | string | 'Breadcrumb' | Accessible name for the nav landmark. |