Breadcrumb
Where a page sits in the site, as real links with no JavaScript. The same trail is written as BreadcrumbList JSON-LD.
server - 0 KB
Breadcrumb
server - 0 KBWhere a page sits in the site, as real links with no JavaScript. The same trail is written as BreadcrumbList JSON-LD.
The code
breadcrumb.tsx
import { Breadcrumb } from 'topclassui/breadcrumb'
/** The last item has no href: it is the page being read, and it stays out of the JSON-LD. */
export default function Demo() {
return (
<Breadcrumb
items={[
{ href: '/', label: 'Home' },
{ href: '/docs', label: 'Docs' },
{ label: 'Breadcrumb' },
]}
/>
)
}
Leave href off the last item: it becomes the current page, and the linked items also emit the BreadcrumbList JSON-LD.
Every state
3 statesExamples
itemsis the trail as{ href?, label }, root first. With anhrefon the last item too, it is one more link and no page is marked current.- In Next.js a plain link reloads the page. Pass
link={<Link href="" />}and each crumb is a client navigation; its href is replaced with the item's. - Pass
baseUrl="https://example.com"and the JSON-LD lists absolute URLs. The links stay as written. - A crumb with an empty label, or one that is not a string, is left out of the trail and of the JSON-LD.
- A crumb without an
hrefthat is not the last one is plain text: no link, and no entry in the JSON-LD. - A long label ends in an ellipsis at the width of its box and is cut at 200 characters. The JSON-LD keeps the whole label.
basic
basic.tsx
import { Breadcrumb } from 'topclassui/breadcrumb'
/** The last item has no href: it is the page being read, and it stays out of the JSON-LD. */
export default function Demo() {
return (
<Breadcrumb
items={[
{ href: '/', label: 'Home' },
{ href: '/docs', label: 'Docs' },
{ label: 'Breadcrumb' },
]}
/>
)
}
all-links
all-links.tsx
import { Breadcrumb } from 'topclassui/breadcrumb'
/** Only the ancestors: every crumb is a link, so nothing is marked as the current page. */
export default function Demo() {
return (
<Breadcrumb
items={[
{ href: '/', label: 'Home' },
{ href: '/shop', label: 'Shop' },
{ href: '/shop/shoes', label: 'Shoes' },
]}
/>
)
}
long-trail
long-trail.tsx
import { Breadcrumb } from 'topclassui/breadcrumb'
/** Nine crumbs in a narrow column: the list wraps, and each caret stays with its crumb. */
export default function Demo() {
return (
<div className="max-w-85 rounded-box border border-line p-2">
<Breadcrumb
items={[
{ href: '/', label: 'Home' },
{ href: '/docs', label: 'Documentation' },
{ href: '/docs/components', label: 'Components' },
{ href: '/docs/components/navigation', label: 'Navigation and disclosure' },
{ href: '/docs/components/navigation/breadcrumb', label: 'Breadcrumb' },
{ href: '/docs/components/navigation/breadcrumb/examples', label: 'Examples' },
{ href: '/docs/components/navigation/breadcrumb/examples/server', label: 'Server rendering' },
{ href: '/docs/components/navigation/breadcrumb/examples/server/json-ld', label: 'Structured data' },
{ label: 'Escaping a label' },
]}
/>
</div>
)
}
long-label
long-label.tsx
import { Breadcrumb } from 'topclassui/breadcrumb'
const LONG_LINK = 'Everything you need to know before migrating a design system'
const LONG_PAGE = 'A step-by-step guide to moving every component to logical properties'
/** A crumb wider than its box ends in an ellipsis instead of pushing the page apart. */
export default function Demo() {
return (
<div className="w-60 rounded-box border border-line p-2">
<Breadcrumb
items={[
{ href: '/', label: 'Home' },
{ href: '/guides/migrating', label: LONG_LINK },
{ label: LONG_PAGE },
]}
/>
</div>
)
}
mixed-label
mixed-label.tsx
import { Breadcrumb } from 'topclassui/breadcrumb'
const NOTES = 'Release notes ملاحظات الإصدار لكل مكوّن في المكتبة'
const PAGE = 'Logical properties الخصائص المنطقية في كل مكوّن'
/** A label that starts in English and goes on in Arabic runs in the page direction: English first, cut at the page's end. */
export default function Demo() {
return (
<div className="w-60 rounded-box border border-line p-2">
<Breadcrumb
items={[
{ href: '/', label: 'Home' },
{ href: '/release-notes', label: NOTES },
{ label: PAGE },
]}
/>
</div>
)
}
deep-trail
deep-trail.tsx
import { Breadcrumb } from 'topclassui/breadcrumb'
const STEPS = [
'Company handbook for every regional office',
'People, payroll and the benefits each country offers',
'Leave, public holidays and working from another country',
'Parental leave and the return-to-work arrangements',
'Eligibility by contract type and length of service',
'How to apply, and who approves the request',
'Documents the payroll team needs before the first month',
'Frequently asked questions from managers and their teams',
'Changes made since the last review of this policy',
'Country annexes and the local rules that override them',
'Annex 7: the Gulf states and the wider Middle East',
'Worked examples for a split leave across two tax years',
]
/** Twelve long crumbs and no box around them: on a phone each takes a row of its own
* and ends in an ellipsis, so the page never scrolls sideways. */
export default function Demo() {
return (
<Breadcrumb
items={STEPS.map((label, index) => (
index === STEPS.length - 1 ? { label } : { href: `/handbook/step-${String(index + 1)}`, label }
))}
/>
)
}
In Arabic, right to left
rtl.tsx
import { ar } from 'topclassui/labels/ar'
import { Breadcrumb } from 'topclassui/breadcrumb'
/** The Arabic labels name the nav, and the caret is flipped so it points along the trail. */
export default function Demo() {
return (
<div dir="rtl" lang="ar">
<Breadcrumb
labels={ar}
items={[
{ href: '/', label: 'الرئيسية' },
{ href: '/docs', label: 'التوثيق' },
{ label: 'مسار التنقل' },
]}
/>
</div>
)
}
Props
Keyboard and accessibility
- The trail is an ordered list inside a nav named "Breadcrumb" from
labels.breadcrumb.label.aria-labelrenames it; an empty one falls back to the built-in name. - The last crumb carries
aria-current="page"and a heavier weight, so colour is not the only cue. - The carets between crumbs are decorative and hidden from a screen reader.
Right to left
Under dir="rtl" the trail runs from the right and each caret flips to point along it. labels={getLabels('ar').labels} names the nav in Arabic.
Server and client
Renders on the server and ships no JavaScript.
Files: index.tsx.
On the chip: server - 0 KB.