Breadcrumb
When to use
Section titled “When to use”- Pages deep in a hierarchy, such as Docs / Components / Breadcrumb
- Letting people go up a level without the back button
Not on a flat site, or as the only navigation.
Choosing between moving around:
| Component | Use it for | For example |
|---|---|---|
| Breadcrumb | Showing where the page sits in a hierarchy, with links up it | Home / Projects / Website |
| Pagination | Moving between pages of one long list | Search results, page 3 of 12 |
| Tabs | Switching between views within one page | Overview / Activity / Settings |
| Link | One destination | “Read the docs” |
import { Breadcrumb } from '@arun-dev/ui';import { docsUrl } from '@/lib/url';
export default function BreadcrumbBasics() { return ( <Breadcrumb.Root> <Breadcrumb.Item> <Breadcrumb.Link href={docsUrl('/')}>Home</Breadcrumb.Link> </Breadcrumb.Item> <Breadcrumb.Item> <Breadcrumb.Link href={docsUrl('/components/button/')}>Components</Breadcrumb.Link> </Breadcrumb.Item> <Breadcrumb.Item> <Breadcrumb.Current>Breadcrumb</Breadcrumb.Current> </Breadcrumb.Item> </Breadcrumb.Root> );}Breadcrumb is a <nav aria-label="Breadcrumb"> around an ordered list of links. The page you
are on comes last, as Breadcrumb.Current: not a link, and marked aria-current="page". The
/ between steps is drawn by CSS and not read out.
Anatomy
Section titled “Anatomy”<Breadcrumb.Root> <Breadcrumb.Item> <Breadcrumb.Link href="/">Home</Breadcrumb.Link> </Breadcrumb.Item> <Breadcrumb.Item> <Breadcrumb.Current>Settings</Breadcrumb.Current> </Breadcrumb.Item></Breadcrumb.Root>Pass your router’s link to Breadcrumb.Link through render, as with Link.
Your own separator
Section titled “Your own separator”The / is the ::before of every step but the first, so a separator of your own is one CSS rule
on a class you give Breadcrumb.Root: a character in content, or an icon as a mask painted with
currentColor, which keeps --breadcrumb-separator-color in light and dark. Keep the / '' after
the content — that empty alternative text is what keeps the separator from being read out.
import type { CSSProperties } from 'react';import { Breadcrumb, Stack } from '@arun-dev/ui';import { docsUrl } from '@/lib/url';// Your own .svg file: `?url` gives its address, as your bundler serves it — a small one inlined as a// data: URL, which is why the url() below is quoted.import arrowUrl from './assets/breadcrumb-arrow.svg?url';
/** * The separator is CSS, the `::before` of every step but the first, so your own is a rule on a * class you give the Root. Keep the `/ ''` alternative text: the separator stays unread. * * A character goes straight into `content`. An icon goes in as a mask, painted with * `currentColor`, so it keeps `--breadcrumb-separator-color` and follows light and dark — inline * in the CSS, or from an .svg file, whose address the third trail passes in as a custom property. */const SEPARATOR_CSS = ` .trail-chevron .breadcrumb-item + .breadcrumb-item::before { content: '›' / ''; }
.trail-icon .breadcrumb-item + .breadcrumb-item::before { content: '' / ''; inline-size: 1em; block-size: 1em; background-color: currentColor; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E") center / contain no-repeat; }
.trail-file .breadcrumb-item + .breadcrumb-item::before { content: '' / ''; inline-size: 1em; block-size: 1em; background-color: currentColor; mask: var(--trail-separator) center / contain no-repeat; }`;
function Trail({ className, label, style,}: { className: string; label: string; style?: CSSProperties;}) { return ( <Breadcrumb.Root className={className} aria-label={label} style={style}> <Breadcrumb.Item> <Breadcrumb.Link href={docsUrl('/')}>Home</Breadcrumb.Link> </Breadcrumb.Item> <Breadcrumb.Item> <Breadcrumb.Link href={docsUrl('/components/button/')}>Components</Breadcrumb.Link> </Breadcrumb.Item> <Breadcrumb.Item> <Breadcrumb.Current>Breadcrumb</Breadcrumb.Current> </Breadcrumb.Item> </Breadcrumb.Root> );}
export default function BreadcrumbSeparator() { return ( <Stack gap="sm"> <style>{SEPARATOR_CSS}</style> {/* Two trails on one page, so each has its own name. */} <Trail className="trail-chevron" label="Breadcrumb, chevron" /> <Trail className="trail-icon" label="Breadcrumb, icon" /> <Trail className="trail-file" label="Breadcrumb, icon file" style={{ '--trail-separator': `url("${arrowUrl}")` } as CSSProperties} /> </Stack> );}.my-trail .breadcrumb-item + .breadcrumb-item::before { content: '›' / '';}An icon from an .svg file of your own works the same way, as a mask. Point at the file from your
stylesheet, or, as the third trail does, import its address and pass it in a custom property:
.my-trail .breadcrumb-item + .breadcrumb-item::before { content: '' / ''; inline-size: 1em; block-size: 1em; background-color: currentColor; mask: url('/icons/arrow.svg') center / contain no-repeat;}As a mask the icon takes the separator’s colour, whatever colour the file draws in. To keep the
file’s own colours instead, put it in content: content: url('/icons/arrow.svg') / ''.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | Concatenated with the component's own classes, never replacing them. | |
render | ReactElement | Element or component to render instead of the default. Props, className, event handlers and ref are merged onto it. | |
ref | Ref<HTMLElement> | Ref to the rendered element. Merged with any ref on the render element. | |
…rest | HTMLAttributes | Anything else is spread onto the rendered element, so id, aria-*, data-* and event handlers all reach the DOM. |
- With two breadcrumbs on a page, give each its own
aria-label. - Restyle with the
--breadcrumb-*tokens, and the separator with a rule of your own (above).