Skip to content

Overlays & Navigation Widgets

Overlay Z-Ordering

Overlays are rendered above the main content at the root level:

  1. Modal - lowest (backdrop covers entire screen)
  2. Popover - middle
  3. Toast - highest (always visible)

Centered dialog overlay. Portals to root level regardless of declaration location. Root-portal modals capture keyboard focus and backdrop pointer routing; if the modal content has no focusable descendants, focus is suspended until the modal is dismissed. Use OverlayScope::Local only for inline composition without full modal focus/backdrop semantics.

PropTypeDescription
titleimpl Into<String>Constructor - dialog title
childElementDialog content
scopeOverlayScopeRootPortal (default) or Local
on_closeCallback<()>Close callback (Esc/backdrop click)
widthLengthDialog width
heightLengthDialog height
backdrop_styleStyleBackdrop overlay style
frame_styleStyleDialog container style
border_styleBorderStyleDialog border
paddingimpl Into<Padding>Dialog inner padding
title_styleStyleTitle style
rust
if ctx.state.show_confirm {
    Modal::new("Confirm Delete")
        .child(
            VStack::new()
                .gap(1)
                .child(Text::new("This action cannot be undone."))
                .child(
                    HStack::new().gap(1)
                        .child(Button::new("Cancel")
                            .on_click(ctx.link().callback(|_| Msg::CancelDelete)))
                        .child(Button::new("Delete")
                            .style(Style::new().fg(Color::White).bg(Color::Red))
                            .on_click(ctx.link().callback(|_| Msg::ConfirmDelete)))
                )
        )
        .on_close(ctx.link().callback(|_| Msg::CancelDelete))
        .into()
}

Toast Notifications

Toasts use ctx.toast() - no view tree setup required.

App configuration:

rust
App::new()
    .toast_placement(ToastPlacement::BottomEnd)  // default
    .toast_gap(1)
    .mount(Root)
    .run()

Showing toasts:

rust
fn update(&mut self, msg: Msg, ctx: &mut Context<Self>) -> Update {
    match msg {
        Msg::SaveSuccess => {
            ctx.toast().push(Toast::new("Saved successfully!"));
            Update::full()
        }
        Msg::SaveError(e) => {
            ctx.toast().push(
                Toast::new(format!("Save failed: {e}"))
                    .title("Error")
                    .border(true)
            );
            Update::full()
        }
        Msg::ShowCustom => {
            let id = ctx.toast().push(
                Toast::new("Custom message")
                    .duration(10.0)
                    .border(true)
            );
            ctx.state.toast_id = Some(id);  // Store to dismiss later
            Update::full()
        }
        Msg::DismissToast => {
            if let Some(id) = ctx.state.toast_id.take() {
                ctx.toast().dismiss(id);
            }
            Update::none()
        }
    }
}

ToastHandle methods:

MethodDescription
.push(Toast)Show toast, returns OverlayId
.dismiss(id)Dismiss a specific toast
.clear()Clear all toasts

ToastPlacement: TopStart, TopCenter, TopEnd, BottomStart, BottomCenter, BottomEnd (default).

Toast props:

PropTypeDescription
messageimpl Into<String>Constructor - toast text
durationf32Auto-dismiss seconds (0 = permanent)
titleStringOptional title
title_prefixStringTitle prefix symbol
title_suffixStringTitle suffix
title_alignmentAlignTitle alignment
title_styleStyleTitle style
message_styleStyleMessage style
frame_styleStyleContainer style
borderboolShow border
border_styleBorderStyleBorder style
paddingimpl Into<Padding>Padding
widthLengthWidth
max_widthLengthMaximum width
wrapboolWrap long messages
decoration / decorationsFrameDecorationEdge decorations

Toasts are suppressed in inline mode to avoid terminal history corruption.


Popover

Floating content panel triggered by an element.

PropTypeDescription
triggerElementThe trigger element
contentElementPopover content
openboolControlled open state
scopeOverlayScopeRootPortal (default) or Local
on_closeCallback<()>Close callback
placementPopoverPlacementAbove, Below, Left, Right + start/center/end variants
offsetu16Distance from trigger
clampboolKeep within screen bounds
auto_flipboolFlip placement when out of bounds
min_trigger_widthboolKeep popover at least as wide as the trigger; content may still make it wider
fit_trigger_widthboolForce popover width to exactly match the trigger, unless capped by max_width
max_widthLengthCap the resolved popover width; percent resolves against overlay bounds
anchorOption<(u16, u16)>Absolute content-coordinate anchor instead of trigger rect

Popover renders through the root overlay pipeline by default, so it appears above normal in-tree content. Use .scope(OverlayScope::Local) when it should stay inside parent stacking order, such as an autocomplete attached to content that can be covered by an inline sidebar layer.

By default, Popover uses .min_trigger_width(true): the overlay is at least as wide as its trigger but can grow wider for long content. Use .fit_trigger_width(true) for exact trigger width, or .max_width(...) to cap content-driven growth.


Tooltip

Help text on hover or focus.

PropTypeDescription
textimpl Into<String>Constructor - tooltip text
childElementThe element to add tooltip to
openboolControlled open state
autoboolAuto-show on hover/focus
text_styleStyleTooltip text style
container_styleStyleTooltip container style
borderboolShow border
border_styleBorderStyleBorder style
paddingimpl Into<Padding>Inner padding
placementPopoverPlacementTooltip placement
offsetu16Distance from element
clampboolKeep within screen bounds
auto_flipboolFlip when out of bounds
rust
Tooltip::new("This button saves your work")
    .auto(true)
    .placement(PopoverPlacement::Top)
    .child(
        Button::new("Save")
            .on_click(ctx.link().callback(|_| Msg::Save))
            .into()
    )

Accordion

Collapsible content sections.

PropTypeDescription
exclusiveboolOnly one section open at a time
gapu16Gap between sections
paddingimpl Into<Padding>Outer padding
borderboolSection border
border_styleBorderStyleSection border style
styleStyleContainer style
header_styleStyleHeader idle style
header_hover_styleStyleHeader hover style
header_focus_styleStyleHeader focus style
header_paddingimpl Into<Padding>Header padding
content_paddingimpl Into<Padding>Content area padding
content_borderboolContent area border
content_styleStyleContent area style
expanded_iconcharExpanded section icon
collapsed_iconcharCollapsed section icon
disabled_styleStyleStyle when disabled
focusableboolWhether headers participate in focus traversal
widthLengthWidth
heightLengthHeight
on_toggleCallback<AccordionEvent>Section toggle event
rust
Accordion::new()
    .exclusive(true)
    .item(AccordionItem::new(
        "Section 1",
        Text::new("Content for section 1").into()
    ))
    .item(AccordionItem::new(
        "Section 2",
        Text::new("Content for section 2").into()
    ))

SearchPalette

Fuzzy search widget powered by nucleo. Composes an Input and List into a filterable, keyboard-navigable search panel.

SearchPalette is not an overlay by itself - wrap it in Modal for the classic command-palette experience, or embed it inline in a Frame, sidebar, or any other container.


CommandPalette

CommandPalette is a composite overlay widget that reads commands from ctx.command_registry() and renders them through SearchPalette<CommandId> inside a Modal.

Use ctx.register_command(...) inside a component to register component-scoped commands, or ctx.command_registry().register(...) for app-wide commands.

PropTypeDescription
on_closeCallback<()>Fired when the modal closes or a command executes
show_disabledboolInclude disabled commands in results (muted and non-activating)
titleimpl Into<RichText>Modal title
widthLengthModal width
heightLengthModal height
scopeOverlayScopeRootPortal (default) or Local
backdrop_styleStyleBackdrop overlay style
frame_styleStyleModal frame style
borderboolShow modal border
border_styleBorderStyleModal border style
paddingimpl Into<Padding>Modal content padding
title_styleStyleTitle style
title_alignmentAlignTitle alignment
rust
fn init(&mut self, ctx: &mut Context<Self>) -> Option<Command> {
    let link = ctx.link().clone();
    ctx.register_command(
        CommandEntry::builder("app.toggle-wrap")
            .label("Toggle word wrap")
            .description("Enable or disable editor wrapping")
            .category("Application")
            .keybinding("p")
            .handler(Callback::new(move |_| link.send(Msg::ToggleWrap)))
            .build(),
    );
    None
}

fn view(&self, ctx: &Context<Self>) -> Element {
    if ctx.state.show_palette {
        CommandPalette::new()
            .title("Commands")
            .show_disabled(true)
            .on_close(ctx.link().callback(|_| Msg::ClosePalette))
            .into()
    } else {
        Element::empty()
    }
}

Command ids are open-ended (CommandId) and can be grouped with optional categories and right-aligned keybinding hints.

Items can be provided flat via .items() or grouped via .entries() using SearchEntry::item(...), SearchEntry::header(...), and SearchEntry::spacer().

Headers and spacers are display-only rows (not searchable/selectable). Group rendering rules:

  • With an empty query, all item rows are shown and headers/spacers render in entry order.
  • With a non-empty query, grouped chrome is hidden and matches render as a flat ranked list.

Item text is typically built from SearchItem::new(label, value) and optional .description("..."). By default, description renders inline as label - description and uses description_style.

You can also add hidden aliases with SearchItem::alias(...) or SearchItem::aliases(...). Aliases are searched and scored like the label, but they are never rendered, which makes them useful for abbreviations, legacy names, or alternate command titles.

You can also mark rows active with .active(true) on SearchItem or SearchEntry::item(...).

Core props

PropTypeDescription
itemsimpl IntoIterator<Item = SearchItem<T>>Flat searchable items (clears entries)
entriesimpl IntoIterator<Item = SearchEntry<T>>Grouped entries via item/header/spacer rows
sync_match_limitusizeMax item count that still matches synchronously (default: 100)
sync_selectionboolKeep on_select synced with the current visible row
initial_queryimpl Into<Arc<str>>Pre-populate search field
initial_selected_item_indexOption<usize>Start selection on this items index when it appears in results (else first row)
placeholderimpl Into<Arc<str>>Input placeholder (default: "Search...")
widthLengthRequested palette width (default: Flex(1))
heightLengthRequested palette height (default: Flex(1))
max_widthLengthMaximum palette width constraint
max_heightLengthMaximum palette height constraint

Callbacks

PropTypeDescription
on_query_changeCallback<Arc<str>>Fired when the query text changes
on_selectCallback<SearchEvent<T>>Fired when selection moves; with sync_selection(true) also fires for initial/result-driven selection
on_activateCallback<SearchEvent<T>>Fired on Enter or double-click

Input forwarding

PropTypeDescription
input_prefiximpl Into<Arc<str>>Prefix before query text (default: " ")
input_suffiximpl Into<Arc<str>>Suffix after query text (default: "{matches}/{total}")
input_borderboolShow input border
input_dividerboolRender divider below input (uncontrolled mode, default: true)
input_divider_styleStyleDivider style below input
input_divider_join_frameboolJoin divider with surrounding frame border (default: true)
input_caret_shapeCaretShapeInput caret shape (Block, Bar, Underline)
input_caret_colorColorInput caret color (OSC 12 cursor color, terminal support required)
input_border_styleBorderStyleInput border style
input_paddingimpl Into<Padding>Input padding
input_styleStyleInput base style
input_hover_styleStyleInput hover style
input_focus_styleStyleInput focus style
input_placeholder_styleStylePlaceholder style
input_focus_placeholder_styleStylePlaceholder style when focused
input_prefix_styleStylePrefix style
input_focus_prefix_styleStylePrefix style when focused
input_suffix_styleStyleSuffix style
input_focus_suffix_styleStyleSuffix style when focused

List forwarding

Forwarded list state-style setters use StyleSlot semantics: list_selection_style, list_item_hover_style, and list_active_style replace theme roles; use matching extend_list_*_style / inherit_list_*_style methods to extend or inherit the scoped theme roles.

PropTypeDescription
list_borderboolShow list border
list_border_styleBorderStyleList border style
list_paddingimpl Into<Padding>List padding
list_styleStyleList base style
list_hover_styleStyleList hover style
list_selection_styleStyleSelected item style
extend_list_selection_style / inherit_list_selection_styleStyle / ()Extend or inherit the selection theme role instead of replacing it
list_unfocused_selection_styleStyleSelected item style while list is not focused; defaults to list_selection_style
extend_list_unfocused_selection_style / inherit_list_unfocused_selection_styleStyle / ()Extend or inherit the unfocused selection theme role instead of replacing it
list_selection_symbolimpl Into<Arc<str>>Selection indicator (default: "> ")
list_selection_symbol_styleStyleSelection indicator style
list_unfocused_selection_symbol_styleStyleSelection indicator style while list is not focused; defaults to list_selection_symbol_style
list_symbol_columnboolControl whether the internal list reserves and renders its symbol/status column
list_unselected_symbolimpl Into<Arc<str>>Non-selected item indent
list_selection_full_widthboolExtend selection to full width
list_item_hover_styleStyleIndividual item hover style
extend_list_item_hover_style / inherit_list_item_hover_styleStyle / ()Extend or inherit the item hover theme role instead of replacing it
list_active_styleStyleActive item style
extend_list_active_style / inherit_list_active_styleStyle / ()Extend or inherit the active item theme role instead of replacing it
list_active_symbolimpl Into<Arc<str>>Active item symbol
list_active_symbol_styleStyleActive symbol style
list_item_horizontal_paddingimpl Into<Padding>Normal row padding (left/right used)
list_header_horizontal_paddingimpl Into<Padding>Header row padding (left/right used)
list_focusableboolAllow list keyboard focus (default: true)
list_scrollbarboolShow scrollbar
list_scrollbar_configScrollbarConfigFull scrollbar configuration (variant, gap, thumb, thumb styles)
empty_textimpl Into<Arc<str>>Empty state text (default: "No matches")
empty_text_styleStyleEmpty state text style

list_item_horizontal_padding and list_header_horizontal_padding accept Padding, but only left and right are applied by List.

SearchPalette forwards the same ListConfig symbol/gutter fields as List: use list_symbol_column(bool) to control the status/selection symbol column, and configure gutter_gap / non-selectable gutter participation with list_config(ListConfig::new().gutter_gap(n).gutter_for_non_selectable(true)). Gutters reserve across participating rows; the default gap is 0.

Item rendering

PropTypeDescription
item_styleStyleItem label base style
header_styleStyleGroup header row style for entries created with SearchEntry::header(...)
description_styleStyleItem description style
description_placementDescriptionPlacementDescription placement: Inline, Right, Above, Below
description_selectionboolWhether selection highlight applies to description text
description_overflowDescriptionOverflowDescription overflow policy: Truncate or Wrap (Wrap applies to Above/Below)
match_styleStyleMatched character style
show_scoresboolShow numeric match scores
score_gradientColorGradientGradient for score coloring
score_range(u64, u64)Explicit score range for gradient
render_itemSearchRenderer<T>Custom item renderer (Arc<dyn Fn>)
item_statusSearchStatusRenderer<T>Add per-item content in the existing list symbol column
item_gutterSearchGutterRenderer<T>Add a per-item left gutter while keeping default row rendering

description_selection(false) has no effect with DescriptionPlacement::Inline and DescriptionPlacement::Right, because selection/hover styling applies to the whole primary row in those modes.

description_selection also controls description hover styling when list_item_hover_style is set.

description_overflow(DescriptionOverflow::Wrap) affects only DescriptionPlacement::Above and DescriptionPlacement::Below; inline and right placement keep single-row truncation behavior.

header_style applies only to generated group headers. Use list_header_horizontal_padding for header row padding.

ItemDescription::right(...) is trailing metadata: it is bounded and truncated before the primary label, so long right-side hints do not displace item labels in narrow palettes.

Use item_status(...) for symbol-column status indicators without replacing the default renderer:

rust
SearchPalette::new()
    .items(items)
    .item_status(Arc::new(|item, _highlight| {
        is_working(&item.value).then(|| Spinner::new().into())
    }))

Use item_gutter(...) when a row-local leading adornment needs its own extra column instead of the selection/status symbol column:

rust
SearchPalette::new()
    .items(items)
    .item_gutter(Arc::new(|item, _highlight| {
        is_working(&item.value).then(|| Spinner::new().into())
    }))

Matching config

PropTypeDescription
case_matchingCaseMatchingCase sensitivity (default: Smart)
normalizationNormalizationUnicode normalization (default: Smart)

Matching uses synchronous updates for lists up to sync_match_limit items and off-thread nucleo searches for larger lists; items do not need Send/Sync.

Standalone ranking - rank_search_palette_indices(&[SearchItem<T>], query) returns each item’s index in the source slice ordered like the palette’s fuzzy results (smart case matching and normalization). Use rank_search_palette_indices_with_score(..., |index, item, score| ...) when another signal should boost or demote matched items before final ordering; NaN adjusted scores rank after finite scores. Use these helpers when another widget owns the query/focus but you need the same ordering for keyboard selection.

As a modal overlay - wrap in Modal and set on_close/size there:

rust
if ctx.state.show_palette {
    let palette = SearchPalette::<Arc<str>>::new()
        .entries(vec![
            SearchEntry::header("Sources"),
            SearchEntry::item("src/lib.rs", Arc::from("src/lib.rs"))
                .description("Crate root"),
            SearchEntry::header("Examples"),
            SearchEntry::item("examples/demo.rs", Arc::from("examples/demo.rs")),
        ])
        .list_scrollbar(true)
        .list_selection_full_width(true)
        .list_item_hover_style(Style::new().bg(Color::DarkGray))
        .on_activate(ctx.link().callback(Msg::Activated));

    Modal::new("Open File")
        .child(palette)
        .width(Length::Px(60))
        .height(Length::Px(20))
        .border_style(BorderStyle::Rounded)
        .padding(0)
        .on_close(ctx.link().callback(|_| Msg::ClosePalette))
}

Inline - embed directly without Modal:

rust
Frame::new()
    .title("Search")
    .border(true)
    .child(SearchPalette::<Arc<str>>::new().items(my_items))

ContextMenu

A popup menu backed by Popover + List. Typically triggered by right-click or a keyboard shortcut.

PropTypeDefaultDescription
triggerimpl IntoElementConstructorThe element that owns the menu
itemsimpl IntoIterator<Item = impl Into<ListItem>>[]Menu items
openboolfalseControlled open state
on_selectCallback<usize>-Fires with selected item index
on_closeCallback<()>-Fires when menu should close
anchorOption<(u16, u16)>NoneAbsolute anchor position (content coordinates)
placementPopoverPlacementBelowStartMenu placement relative to trigger
offsetimpl Into<PopoverOffset>0Distance from trigger
clampbooltrueKeep within screen bounds
auto_flipbooltrueFlip placement when out of bounds
widthLengthPx(20)Menu width
heightLengthAutoMenu height
borderbooltrueShow border
border_styleBorderStylePlainBorder style
paddingimpl Into<Padding>defaultInner padding
styleStyledefaultBase style
selection_styleStyledefaultSelected item style
extend_selection_style / inherit_selection_styleStyle / ()-Extend or inherit the selection theme role instead of replacing it
unfocused_selection_styleStyle-Selected item style while menu list is not focused; defaults to selection_style
extend_unfocused_selection_style / inherit_unfocused_selection_styleStyle / ()-Extend or inherit the unfocused selection theme role instead of replacing it
item_hover_styleStyle-Hovered item style
extend_item_hover_style / inherit_item_hover_styleStyle / ()-Extend or inherit the hover theme role instead of replacing it
selection_symbolOption<impl Into<Arc<str>>>"> "Selection indicator
selection_symbol_styleStyle-Selection indicator style
unfocused_selection_symbol_styleStyle-Selection indicator style while menu list is not focused; defaults to selection_symbol_style
scrollbarboolfalseShow scrollbar
scrollbar_configScrollbarConfigdefaultScrollbar configuration
rust
ContextMenu::new(
    Button::new("Options").on_click(ctx.link().callback(|_| Msg::ToggleMenu))
)
    .items(vec!["Cut", "Copy", "Paste", "Delete"])
    .open(ctx.state.menu_open)
    .on_select(ctx.link().callback(Msg::MenuAction))
    .on_close(ctx.link().callback(|_| Msg::CloseMenu))
    .selection_style(Style::new().bg(Color::DarkGray))

MIT OR Apache-2.0