Navigation
Breadcrumb
Shows where the current view sits in a hierarchy, with a link to each level above it.


use iced::widget::{column, text};
use iced::{Element, Length};
use iced_cube::{breadcrumb, crumb, lucide};
const PATH: [&str; 4] = ["Home", "Documents", "Invoices", "2024"];
#[derive(Debug, Clone)]
pub enum Message {
Open(usize),
}
#[derive(Debug)]
pub struct Example {
depth: usize,
}
impl Default for Example {
fn default() -> Self {
Self { depth: PATH.len() }
}
}
impl Example {
pub fn update(&mut self, message: Message) {
let Message::Open(index) = message;
self.depth = index + 1;
}
pub fn view(&self) -> Element<'_, Message> {
let crumbs = PATH[..self.depth].iter().enumerate().map(|(index, name)| {
let crumb = crumb(*name).on_press(Message::Open(index));
if index == 0 {
crumb.icon(lucide!(House))
} else {
crumb
}
});
let folder = format!("Viewing {}", PATH[..self.depth].join("/"));
column![breadcrumb(crumbs), text(folder).size(14)]
.spacing(12)
.width(Length::Fill)
.max_width(360)
.into()
}
}Usage
The app holds the path and builds the trail from it on every view, giving each level above the current one a message that opens it. The breadcrumb only draws the trail, so the levels can be folders, settings sections or records.
On a narrow screen the trail stays on one line by hiding more of its middle behind the ellipsis, down to the root and the current view, and only wraps if even that does not fit. Labels are never squeezed. Once someone expands the trail, .collapse_to_fit(false) shows every crumb and lets it wrap instead. For deep hierarchies, .max_items keeps the root and the levels nearest the current view, which are the ones people go back to most, and .on_expand lets them see the rest.
The keymap’s actions return an index rather than an event, because the app owns the path: action.target(len) says which crumb to open, as the keyboard example shows.
Examples
Collapsed
A long trail with slash separators keeps its first and last levels. The ellipsis shows the rest.


use iced::widget::column;
use iced::{Element, Length};
use iced_cube::navigation::breadcrumb::Separator;
use iced_cube::{breadcrumb, crumb};
const TRAIL: [&str; 6] = [
"Workspace",
"Engineering",
"Platform",
"Services",
"Billing",
"Settings",
];
#[derive(Debug, Clone)]
pub enum Message {
Open(usize),
Expand,
}
#[derive(Debug, Default)]
pub struct Example {
expanded: bool,
}
impl Example {
pub fn update(&mut self, message: Message) {
// Opening a level collapses the trail again.
self.expanded = matches!(message, Message::Expand);
}
pub fn view(&self) -> Element<'_, Message> {
let crumbs = TRAIL
.iter()
.enumerate()
.map(|(index, name)| crumb(*name).on_press(Message::Open(index)));
let mut trail = breadcrumb(crumbs).separator(Separator::Slash);
trail = if self.expanded {
trail.collapse_to_fit(false)
} else {
trail.max_items(3).on_expand(Message::Expand)
};
column![trail].width(Length::Fill).max_width(420).into()
}
}Keyboard shortcuts
Alt+Up goes to the parent level and Alt+Shift+Up to the root, through the default keymap.


use iced::widget::{column, text};
use iced::{Element, Length, Subscription};
use iced_cube::keys::{self, Keymap};
use iced_cube::navigation::breadcrumb::{self, Action};
use iced_cube::{crumb, lucide};
const PATH: [&str; 5] = ["Library", "Music", "Albums", "1997", "Disc one"];
#[derive(Debug, Clone)]
pub enum Message {
Open(usize),
Key(keys::Event),
}
#[derive(Debug)]
pub struct Example {
depth: usize,
keymap: Keymap<Action>,
}
impl Default for Example {
fn default() -> Self {
Self {
depth: PATH.len(),
keymap: breadcrumb::default_keymap(),
}
}
}
impl Example {
pub fn update(&mut self, message: Message) {
let target = match message {
Message::Open(index) => Some(index),
Message::Key(key) => self
.keymap
.resolve_event(&key)
.and_then(|action| action.target(self.depth)),
};
if let Some(index) = target {
self.depth = index + 1;
}
}
pub fn view(&self) -> Element<'_, Message> {
let crumbs = PATH[..self.depth].iter().enumerate().map(|(index, name)| {
let crumb = crumb(*name).on_press(Message::Open(index));
if index == 0 {
crumb.icon(lucide!(Library))
} else {
crumb
}
});
column![
breadcrumb::breadcrumb(crumbs),
text("Alt+Up goes up a level and Alt+Shift+Up to the top.").size(14),
]
.spacing(12)
.width(Length::Fill)
.max_width(380)
.into()
}
pub fn subscription(&self) -> Subscription<Message> {
keys::subscription().map(Message::Key)
}
}API
| Item | Description |
|---|---|
breadcrumb(crumbs) | Creates a trail from the root to the current view. The last crumb is the current view and never responds to clicks. |
crumb(label) | Creates a crumb. It is plain text until it has a message. |
.on_press(message) / .on_press_maybe(option) | Makes a crumb a link that sends the message. |
.icon(glyph) | Adds a Lucide icon before a crumb's label, such as a house for the root. |
.separator(Separator) | Chevron or Slash between crumbs. Defaults to Chevron. |
.max_items(n) | Shows at most n crumbs: the first and the last n - 1, with an ellipsis for the rest. n is at least 2. |
.on_expand(message) | Makes the ellipsis a button that sends the message, such as one that shows the whole trail. |
.collapse_to_fit(bool) | Whether a trail too wide for one line hides more of its middle until it fits. Defaults to true. Turn it off to show every crumb, wrapping onto more lines. |
breadcrumb:: | The crumb limits a trail tries, longest first, when collapsing to fit. |
breadcrumb:: | The crumbs and ellipsis a trail of len shows for a limit, for custom layouts. |
breadcrumb:: | The default shortcuts as a Keymap<Action>. Bind, unbind or clear chords to change them. |
action.target(len) | The index of the crumb a resolved Action (Parent or Root) goes to in a trail of len, or None at the top. |
Keyboard
Default shortcuts. Every one can be changed or removed with a keymap; see Keyboard shortcuts.
| Keys | Action |
|---|---|
| Alt+ArrowUp | Parent Goes up one level, to the crumb before the current one. |
| Alt+Shift+ArrowUp | Root Goes to the first crumb. |