Breadcrumb
Free4 visual variants, 5 separator styles, a sibling-page dropdown switcher, badges, disabled crumbs, collapsible middle crumbs, a loading skeleton, and full keyboard support.
import { SvBreadcrumbComponent } from '@styloviz/breadcrumb';Demo & usage
Basic usage
Usage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Collapsed with chevrons
<sv-breadcrumb [items]="crumbs" separator="chevron" [maxVisible]="4" (crumbClick)="go($event)" />
Toggleable ellipsis
<sv-breadcrumb [items]="crumbs" [maxVisible]="3" [expandable]="true" (expand)="onExpand()" (collapse)="onCollapse()" />
Pills variant with home icon
<sv-breadcrumb [items]="crumbs" variant="pills" [showHomeIcon]="true" />
Sibling page switcher
<sv-breadcrumb [items]="crumbsWithMenu" (menuItemSelect)="onSwitch($event)" />
Loading skeleton
<sv-breadcrumb [items]="crumbs" [loading]="isLoading()" [skeletonCount]="4" />
Derived from the route, not hand-passed
// The breadcrumb IS the route. A hand-passed array next to it is a // second source of truth, and the gap between them is the whole // "the breadcrumb says the wrong thing" bug class. readonly crumbs = computed(() => svTrailFromPath(this.url(), { root: { label: 'Dashboard', routerLink: '/admin' }, base: '/admin', labels: { '1042': 'Order #1042' }, }));
Router transitions instead of page loads
// app.config.ts — @styloviz/breadcrumb has no dependency on // @angular/router, so the router half is yours. A FACTORY: inject() // inside href/navigate would run outside an injection context. provideSvLinkAdapter(() => { const router = inject(Router); const seg = (t: SvLinkTarget) => (typeof t === 'string' ? [t] : [...t]); return { kind: 'angular-router', href: (t) => router.serializeUrl(router.createUrlTree(seg(t))), navigate: (t) => { void router.navigate(seg(t)); return true; }, }; })
When to use
- Job: destination links — renders real `<a href>` crumbs to levels of a hierarchy the route already defines (compare all seven in the “Choosing a navigation pattern” guide under Guides).
- Showing the user's location within a nested hierarchy and offering a way back up.
- Multi-level apps: settings, file systems, category drill-downs.
- Use the sibling-page `menu` on a crumb when users need to jump between sibling sections (orgs, branches, workspaces) without leaving the current level.
- For switching between sibling views, use Tabs instead.
Async states
- loading
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| items | BreadcrumbItem[] | [] | Ordered list of crumbs. The last item is treated as the current page. |
| separator | BreadcrumbSeparator | 'chevron' | Separator style between crumbs. Use `custom` with `customSeparatorIcon`. |
| customSeparatorIcon | string | '' | Icon-library NAME used when `separator="custom"` — e.g. `'chevron-right'`. Was `customSeparatorPath`, a raw `<path d="…">`, and it was written as a STATIC attribute at every call site, so the rename could not error: the custom-separator demo silently fell back to the default chevron until the sites were rewritten by hand. |
| variant | BreadcrumbVariant | 'plain' | Visual style of the crumb trail. |
| size | BreadcrumbSize | 'md' | Size of the breadcrumb text and icons. |
| maxVisible | number | 0 | Maximum number of crumbs to show before collapsing the middle ones. Set to `0` to always show all. |
| responsive | boolean | false | Collapse to fit the available width instead of to a fixed count. Overrides `maxVisible` when on. |
| expandable | boolean | true | Allow the collapsed ellipsis to be clicked to reveal the hidden middle crumbs. When `false` the ellipsis is purely decorative. |
| showHomeIcon | boolean | false | Prepends a home icon to the first crumb when it has no explicit `icon`. |
| truncateLabels | boolean | false | Clips long labels to a fixed width with an ellipsis and a title tooltip. |
| collapseOnMobile | boolean | false | Hides middle crumbs below the `sm` breakpoint (pure CSS) to keep long trails compact on phones. |
| loading | boolean | false | Shows an animated skeleton in place of the crumb trail while data is loading. |
| skeletonCount | number | 3 | Number of skeleton segments rendered when `loading` is `true`. |
| ariaLabel | string | 'Breadcrumb' | Accessible label for the breadcrumb navigation landmark. |
| customClass | string | '' | Additional CSS classes on the root `<nav>` element. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed load. |
| crumbClick | BreadcrumbItem | Emitted when a crumb with a `routerLink` is clicked. |
| expand | void | Emitted when the collapsed ellipsis is expanded to reveal hidden crumbs. |
| collapse | void | Emitted when an expanded trail is collapsed back via the toggle. |
| menuItemSelect | BreadcrumbMenuItem | Emitted when a sibling page is chosen from a crumb's dropdown switcher. |
Wire it to your API
This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.
Template
Bound to the names this component actually exposes.
<sv-breadcrumb [items]="data.items()" [loading]="data.loading()" [error]="data.error()" (retry)="data.reload()" />
Host class
Drive the three async inputs from your own fetch.
// This component reports its state but does not emit a query, so drive // it from your own fetch and bind the three async inputs. readonly items = signal<Row[]>([]); readonly loading = signal(false); readonly error = signal<SvDataError | null>(null); async load(): Promise<void> { this.loading.set(true); this.error.set(null); try { this.items.set(await this.api.fetchRows()); } catch (e) { this.error.set(toSvDataError(e)); } finally { this.loading.set(false); } }
- This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `items`, `loading` and `error`.
- Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.
Full walkthrough: Connecting to a backend.
Customization & theming
- Four visual variants: plain, contained, pills, underline.
- Five separator styles, including a custom SVG path.
- Collapse middle crumbs behind a toggleable ellipsis on overflow, or hide them below the sm breakpoint with `collapseOnMobile`.
- Optional leading icons, an automatic home icon via `showHomeIcon`, and per-crumb badges.
- Sibling-page dropdown switcher via a crumb's `menu` array.
- Label truncation with a tooltip via `truncateLabels`.
- Customisable nav landmark label via `ariaLabel`.
- Links render through `SvLinkAdapter` from `@styloviz/core` — no dependency on `@angular/router`, so it drops into an app that routes any way at all.
- `svTrailFromPath(path, { labels, root, base, titleCase })` derives the trail from a URL path, with no router of its own.
Best practices
- Do: Make every crumb except the last a working link to its level. `svTrailFromPath` does this for you, and derives the trail from the current path so it cannot disagree with the route.
- Do: Provide `provideSvLinkAdapter()` at the app root if you use a router. Without it the crumbs are still real, shareable links, but following one is a full page load — the component warns once in a dev build to say so.
- Do: Collapse the middle on long paths: `responsive` measures the space and fits as many crumbs as it can, or `maxVisible` sets a fixed count. Collapsing keeps root, current and then the parent — the "up one level" target people reach for.
- Do: Use `loading` with a matching `skeletonCount` while the real path resolves asynchronously, instead of rendering an empty nav.
- Don't: Don't link the current page — mark it with aria-current (handled).
- Tip: Set `disabled: true` on a crumb instead of omitting `routerLink` when a section exists but is permission-gated — it renders with `aria-disabled` and stays out of the tab order.
- Tip: The `maxVisible` ellipsis is a real toggle — clicking it again after expanding collapses the middle crumbs back; listen for `expand`/`collapse` if you need to track its open state.
Accessibility
- Wrapped in a labelled nav landmark (`ariaLabel`).
- Current page marked with aria-current.
- Disabled crumbs render as non-interactive elements with `aria-disabled`, outside the tab order.
- The ellipsis toggle exposes `aria-expanded` and stays focusable in both states, so it can be opened and closed again from the keyboard without losing its place in the tab order.
- Sibling-page dropdown follows the ARIA menu-button pattern: `aria-haspopup`, `aria-expanded`, `aria-controls`, roving tabindex, and ArrowUp/ArrowDown/Home/End/Escape keyboard support with focus returned to the trigger on close.
- The dropdown panel is rendered in a `document.body` portal (dropdown-instruction.md pattern) so it always escapes ancestor `overflow`/transform clipping instead of being cut off inside scrollable cards or tables; it repositions on scroll and closes on viewport resize or outside click.
- Loading state exposes `role="status"` / `aria-busy` with a screen-reader-only "Loading breadcrumb…" label.
Feedback on Breadcrumb
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.