Breadcrumb
The path to where you are, whose middle collapses into one affordance rather than off the edge of the strip.
import { Breadcrumb } from "@glint/components/breadcrumb.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 620px; height: 180px; background: Tokens.color-background;
property <string> went: "Nowhere yet.";
VerticalLayout { padding: 24px; spacing: 16px; alignment: center;
Breadcrumb { accessible-label: "Breadcrumb"; items: [ { label: "Home" }, { label: "Documents" }, { label: "Invoices" }, { label: "INV-0042" }, ]; navigate(index) => { root.went = "Followed crumb " + index + "."; } }
Text { text: root.went; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; } }}Usage
import { Breadcrumb } from "@glint/components/breadcrumb.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component AppWindow inherits Window { width: 520px; height: 120px; background: Tokens.color-background;
VerticalLayout { padding: 24px; alignment: center;
Breadcrumb { accessible-label: "Breadcrumb"; items: [ { label: "Home" }, { label: "Settings" }, { label: "Billing" }, ]; navigate(index) => { debug("go to", index); } } }}items is the trail, left to right. The last item is where you are: it
renders as plain foreground text, takes no tab stop and fires nothing. Every
crumb before it is a link that fires navigate with its index.
A BreadcrumbItem carries two fields:
| Field | What it is |
|---|---|
label | The crumb’s text, and the name assistive technology announces |
menu | Sibling pages hanging off this crumb — a list of MenuSubEntry, the row every Glint menu is built from. Non-empty turns the crumb from a link into a trigger |
Following the crumb is yours. The component reports where the reader wanted to
go and changes nothing about the trail: items is what you hand it after the
route changes.
Examples
The separator
separator is the glyph between two crumbs, and it is a lucide icon rather
than a string — the default is IconSet.ChevronRight. An empty Icon draws
none and takes no column, which is how a trail turns the glyph off instead of
swapping it.
import { Breadcrumb, BreadcrumbItem } from "@glint/components/breadcrumb.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 620px; height: 240px; background: Tokens.color-background;
property <[BreadcrumbItem]> trail: [ { label: "Home" }, { label: "Projects" }, { label: "Glint" }, ];
VerticalLayout { padding: 24px; spacing: 20px; alignment: center;
Breadcrumb { accessible-label: "Chevron"; items: root.trail; }
Breadcrumb { accessible-label: "Slash"; items: root.trail; separator: IconSet.Slash; }
Breadcrumb { accessible-label: "No separator"; items: root.trail; separator: { paths: [] }; } }}A trail too long to draw
max-visible caps how many crumbs render whole. Past it the middle of the
trail leaves the strip and one ellipsis affordance stands where it was, keeping
the first crumb and the last max-visible - 1. Two is the floor: a shorter cap
would elide the current page along with the middle.
The ellipsis is a control, not a decoration — the run is reachable, not merely
gone. Activating it opens the crumbs that left the strip as a picker, and
taking one fires navigate with its place in the whole trail, not its place in
the picker.
import { Breadcrumb } from "@glint/components/breadcrumb.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 620px; height: 220px; background: Tokens.color-background;
property <string> went: "Open the ellipsis to reach the middle of the trail.";
VerticalLayout { padding: 24px; spacing: 16px; alignment: center;
Breadcrumb { accessible-label: "Deep trail"; max-visible: 3; items: [ { label: "Home" }, { label: "Workspace" }, { label: "Engineering" }, { label: "Platform" }, { label: "Releases" }, { label: "v1.4.0" }, ]; navigate(index) => { root.went = "Followed crumb " + index + " of the whole trail."; } }
Text { text: root.went; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; } }}A crumb that opens its siblings
Give a crumb a menu and it stops being a link: it draws a chevron, announces
itself expandable, and activating it opens a picker of the sibling pages under
it. Taking one fires menu-selected(crumb, row) — never navigate, the way a
menu row carrying children never fires itself (ADR-0020). The current page
never becomes a trigger.
The rows are plain commands rather than checkable ones. A write into a nested
array has nowhere to land in Slint (ADR-0020), which is the same reason
MenuSubEntry carries no checkbox state — a picker of destinations is all this
is.
import { Breadcrumb } from "@glint/components/breadcrumb.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 620px; height: 220px; background: Tokens.color-background;
property <string> picked: "Open “Components” to see its siblings.";
VerticalLayout { padding: 24px; spacing: 16px; alignment: center;
Breadcrumb { accessible-label: "Docs"; items: [ { label: "Docs" }, { label: "Components", menu: [ { label: "Guides" }, { label: "Theming" }, { label: "Live preview" }, ] }, { label: "Breadcrumb" }, ]; menu-selected(crumb, row) => { root.picked = "Row " + row + " of crumb " + crumb + "."; } }
Text { text: root.picked; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; } }}Both at once
One picker serves both jobs, so the keyboard, the surface and the highlight are decided once. A trail may elide its middle and still carry menus on the crumbs that stayed.
import { Breadcrumb } from "@glint/components/breadcrumb.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 640px; height: 200px; background: Tokens.color-background;
VerticalLayout { padding: 24px; alignment: center;
Breadcrumb { accessible-label: "Repository"; max-visible: 3; separator: IconSet.ChevronRight; ellipsis-label: "Show the folders in between"; items: [ { label: "thelipe7" }, { label: "glint-ui" }, { label: "slint" }, { label: "components", menu: [ { label: "internal" }, { label: "theme" }, ] }, { label: "breadcrumb.slint" }, ]; } }}API Reference
Properties
| Property | Type | Default | Description |
|---|---|---|---|
items | in [BreadcrumbItem] | no default | Crumbs left-to-right; the last item renders as the current page. |
separator | in LucideIcon | IconSet.ChevronRight | Glyph drawn between crumbs. An empty Icon draws none. |
max-visible | in int | 0 | Longest trail rendered whole; 0 renders every crumb. Past it the middle collapses into the ellipsis. |
ellipsis-label | in string | @tr("Show hidden crumbs") | Name of the ellipsis affordance; override to translate. |
highlighted-index | in-out int | 0 | Row the keyboard stands on inside an open picker; consumers rarely set this. |
Callbacks
| Callback | Description |
|---|---|
navigate(int) | Fired when a crumb is clicked; argument is its index. A picked row of the ellipsis picker fires it too, with the crumb's place in items. |
menu-selected(int , int) | Fired with the crumb a picker hung off and the row taken from it. |
BreadcrumbItem is a data type rather than a component; its two fields are in
Usage above. MenuSubEntry, the row a crumb’s picker draws, is
documented on MenuPanel’s page.
Accessibility
- A navigation landmark. The trail carries
accessible-role: navigation; naming it is the call site’s, so a page with a breadcrumb and a header nav tells them apart. - Earlier crumbs are named controls. Each is announced by its
label, answers the accessible default action, and is a tab stop a keyboard user reaches with Tab. Enter and Space follow the focused crumb. - A crumb with a menu says so. It reports
accessible-expandable, andaccessible-expandedwhile its picker is open. Enter or Space opens the picker rather than following the crumb. - The current page is text. The last crumb declares no role of its own, so it is announced as text and never as a control. It keeps its sizing box and still absorbs the clicks that land on it, so nothing behind the trail receives them.
- The ellipsis is a control. It is named by
ellipsis-label(@tr("Show hidden crumbs")by default), reports itself expandable, and opens the elided run as the same picker. - The picker is a list. It carries its row count and every row is a named list item. Inside it, ↑ and ↓ move the highlight and wrap at both ends, Home and End jump to its ends, Enter or Space takes the highlighted row, and Esc closes it — the walk every Glint list surface shares.