Skip to content

Breadcrumb Navigation

YuuvisBreadcrumbComponent renders an accessible breadcrumb trail that shows the user’s position in a hierarchical navigation path — for example, Case #42 / Review / Contract.pdf. It is part of @yuuvis/client-framework and is available as a standalone Angular component.

The component renders a flat, ordered list of items. All items except the last are interactive links. The last item represents the current location and is displayed as plain text. Clicking or pressing Enter on a link emits a navigate event — the parent component is always responsible for the actual routing. The breadcrumb performs no navigation itself.

Use yuv-breadcrumb whenever a user navigates into nested object hierarchies and needs a way to understand where they are and return to a parent level. A typical scenario is a detail page for a document stored inside a multi-level structure (e.g. a folder, a sub-folder, then the document itself) — the breadcrumb shows the full path and lets the user jump back to any ancestor.

The component requires two things from the host:

  • A BreadcrumbItem[] input — an ordered array of { id, name } objects that describe the trail, built from your domain model. The full interface is described in The BreadcrumbItem Interface.
  • A (navigate) output handler — the callback that executes routing when the user activates a link.
  • A working yuuvis shell client project (see Quick Start)
  • @yuuvis/client-framework installed as a project dependency

YuuvisBreadcrumbComponent and BreadcrumbItem are exported from the breadcrumb entry point of @yuuvis/client-framework. Add both to your component’s imports array:

your-component.ts
import { YuuvisBreadcrumbComponent, BreadcrumbItem } from '@yuuvis/client-framework/breadcrumb';
@Component({
// ...
imports: [YuuvisBreadcrumbComponent],
})
export class YourComponent {
items: BreadcrumbItem[] = [];
}

Each item in the trail is a plain object with two properties:

export interface BreadcrumbItem {
id: string;
name: string;
}
PropertyTypeDescription
idstringA unique identifier for this item. Emitted via the navigate output when the user activates the link. Use a value that lets your navigation handler identify the target — for example, the object’s database ID or route segment.
namestringThe display label rendered in the trail. Displayed as-is — no truncation, translation, or fallback is applied by the component.

Position in the array determines rendering: all items except the last become clickable links; the last item is rendered as plain text and marked as the current page with aria-current="page".

The items input is a plain BreadcrumbItem[] — you build it in your component from whatever domain data is available. The component only renders what you pass; it never fetches or infers hierarchy on its own.

For a simple two- or three-level path, construct the array inline or as a computed signal:

document-details.component.ts
import { computed } from '@angular/core';
import { BreadcrumbItem } from '@yuuvis/client-framework/breadcrumb';
// Inside your component:
// (folder and document are signal inputs or computed signals defined elsewhere in the component)
readonly breadcrumbItems = computed<BreadcrumbItem[]>(() => {
const folder = this.folder();
const doc = this.document();
if (!folder || !doc) return [];
return [
{ id: folder.id, name: folder.name }, // ← link
{ id: doc.id, name: doc.title }, // ← current location
];
});
document-details.component.html
<yuv-breadcrumb
[items]="breadcrumbItems()"
(navigate)="onBreadcrumbNavigate($event)"
/>

When the depth depends on the object model at runtime, build the array programmatically. The following example shows a three-tier model where an optional intermediate level exists between a parent container and its documents — replace actionObj with whatever intermediate node your domain provides (it could be a folder, a phase, a sub-category, or any other level):

document-details.component.ts
readonly breadcrumbItems = computed<BreadcrumbItem[]>(() => {
const parent = this.parentObject();
const actionObj = this.actionObject();
const doc = this.documentObject();
if (!parent || !doc) return [];
const items: BreadcrumbItem[] = [
{ id: parent.id, name: parent.title },
];
if (actionObj) {
items.push({ id: actionObj.id, name: actionObj.title });
}
items.push({ id: doc.id, name: doc.title }); // always last = current location
return items;
});

The same pattern scales to any number of levels — iterate over an ancestor chain and push each level as a BreadcrumbItem, then append the current object last.

When the user clicks or presses Enter on a breadcrumb link, the component emits the full BreadcrumbItem for that link via the (navigate) output. Your handler receives the item and is responsible for executing the actual navigation.

Use this when each breadcrumb item maps directly to its own route. The most common handler navigates to the target object using the Angular Router:

document-details.component.ts
import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { BreadcrumbItem } from '@yuuvis/client-framework/breadcrumb';
// Inside your component:
readonly #router = inject(Router);
onBreadcrumbNavigate(item: BreadcrumbItem): void {
this.#router.navigate(['/objects', item.id]);
}

item.id is whatever value you put into the BreadcrumbItem when building the array, so it can be a route segment, a database ID, or any other application-specific key.

Use this when the trail contains items that all live inside the same parent page, and clicking an intermediate item should open that parent page with a specific node pre-selected. For example: a document’s detail page shows the path Container / Sub-folder / Document.pdf. Clicking Sub-folder should open the container page and automatically highlight the sub-folder in its navigation tree.

The navigate handler always routes to the parent container (/containers/parentId). When the clicked item is the parent itself, the navigation is plain — no query parameter is needed. When the clicked item is an intermediate node (not the parent), the node’s ID is passed as a selected-node-id query parameter so the container page can apply the selection once its data is loaded:

document-details.component.ts
// parentObject is a Signal<ContainerObject | undefined> defined in your component —
// its id is the route target for all breadcrumb links on this page.
onBreadcrumbNavigate(item: BreadcrumbItem): void {
const parentId = this.parentObject()?.id;
if (!parentId) return;
const isParent = item.id === parentId;
// Navigate to the parent container page.
// If the clicked item IS the parent, no query params are needed.
// If the clicked item is an intermediate node, pass its id so the
// container page can pre-select it in its navigation tree.
this.#router.navigate(
['/containers', parentId],
isParent ? undefined : { queryParams: { 'selected-node-id': item.id } }
);
}

Use this in self-contained, in-page navigation where clicking a breadcrumb link collapses the trail rather than routing to a new page — for example, a wizard-style component where each step drills deeper and clicking back in the breadcrumb undoes steps.

Maintain the trail as a signal<BreadcrumbItem[]> initialized from the full initial path. When a link is clicked, slice the array to include only items up to and including the clicked one — that item then becomes the new current location (last item):

breadcrumb-page.component.ts
import { signal } from '@angular/core';
import { BreadcrumbItem } from '@yuuvis/client-framework/breadcrumb';
// Inside your component:
// initialTrail is the starting BreadcrumbItem[] for this page — e.g. the full path
// from the root down to the current location when the user arrived at the page.
readonly trail = signal<BreadcrumbItem[]>([...this.initialTrail]);
navigateToLevel(item: BreadcrumbItem): void {
const idx = this.trail().findIndex(i => i.id === item.id);
if (idx >= 0) {
this.trail.set(this.trail().slice(0, idx + 1));
}
}

Bind the signal and handler in the template:

breadcrumb-page.component.html
<yuv-breadcrumb
[items]="trail()"
(navigate)="navigateToLevel($event)"
/>

The trail collapses to the clicked level, and the last item becomes the new current location.

The component determines clickability by position only: all items except the last render as clickable links; only the last item renders as plain text. There is no built-in support for a display-only label in the middle of the trail.

If your domain model includes intermediate classification labels or category names that have no navigation target, use a handler guard pattern: give each such item a sentinel id value that your handler recognises and ignores.

document-details.component.ts
// A sentinel id that signals "this item is a label, not a navigation target"
const LABEL_SENTINEL = '__label__';
// Build the trail — the phase name is a display-only classification label
readonly breadcrumbItems = computed<BreadcrumbItem[]>(() => {
const items: BreadcrumbItem[] = [
{ id: 'container-1', name: 'Archive' },
{ id: LABEL_SENTINEL, name: 'Confidential' }, // display-only, no route target
{ id: 'doc-42', name: 'Report.pdf' }, // current location
];
return items;
});
// Guard: ignore items that have no navigation target
onBreadcrumbNavigate(item: BreadcrumbItem): void {
if (item.id === LABEL_SENTINEL) return;
this.#router.navigate(['/objects', item.id]);
}

The label item renders visually identical to a real link — same hover underline, same keyboard-focusability — because the component applies no disabled or suppressed styling for non-last items. Activating it has no effect only because the handler returns early. This is a known UX trade-off: if the visual distinction matters for your use case, add custom CSS targeting the specific item or restructure the trail so that label-only levels always appear last.

A breadcrumb with a single item renders only the current location — nothing is clickable and no (navigate) event is ever emitted. Use this when you want to display a title consistently using the breadcrumb’s styling even when there is no parent to navigate to:

readonly breadcrumbItems = computed<BreadcrumbItem[]>(() => {
const obj = this.currentObject();
if (!obj) return [];
return [{ id: obj.id, name: obj.title }]; // single item = current location only
});
<yuv-breadcrumb [items]="breadcrumbItems()" />

The (navigate) binding can be omitted entirely when the trail will never have more than one item.

yuv-breadcrumb exposes three CSS custom properties. Override them on any ancestor element or directly on the yuv-breadcrumb host to change the appearance:

CSS Custom PropertyDefaultControls
--yuv-breadcrumb-link-colorinheritColor of clickable link items
--yuv-breadcrumb-current-colorcurrentColorColor of the current-location item (also font-weight: 500)
--yuv-breadcrumb-separator-colorcurrentColor at 40% opacityColor of the / separator

Apply overrides in your component’s SCSS:

your-component.scss
.breadcrumb-wrapper {
--yuv-breadcrumb-link-color: var(--your-brand-color);
--yuv-breadcrumb-current-color: var(--your-text-emphasis-color);
--yuv-breadcrumb-separator-color: var(--your-muted-color);
}
your-component.html
<div class="breadcrumb-wrapper">
<yuv-breadcrumb [items]="breadcrumbItems()" (navigate)="onBreadcrumbNavigate($event)" />
</div>