Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Notifications

Toasts, notification centers, banners, and inline alerts for temporary and persistent messaging.

Toast system

Toasts are temporary overlays that slide in, show a message, and auto-dismiss. The toast system has three pieces: Toast (one message), Toaster (state that owns the queue and handles timing), and ToastStack (the widget that renders them).

extern crate tuile;
extern crate ratatui_core;
use tuile::prelude::*;
use std::time::Instant;

fn demo() {
let mut toaster = Toaster::new();
toaster.push(Toast::new("Saved", "File written successfully").variant(Variant::Success));
toaster.info("Quick message");

// In your app loop:
let area = Rect::new(0, 0, 80, 24);
let mut buf = Buffer::empty(area);
let now = Instant::now();
toaster.tick(now);
ToastStack::new().now(now).render(area, &mut buf, &mut toaster);
}
fn main() {}

Lifecycle

A toast enters the queue when you call toaster.push(toast). The tick(now) call checks expiry and removes closed toasts. ToastStack renders the visible subset (newest first), applies the animation, and tracks hover state for pause-on-hover. Call tick once per frame with the current time, and render the stack over your main UI.

Creating toasts

Toast::new(title, message) takes two strings. The title is bold, the message is regular weight. Defaults: 5 second timeout, dismissible, no icon, Card style. Builder methods adjust these:

MethodEffect
variant(Variant)Changes color (Default, Primary, Success, Warning, Error, Accent). Default is Variant::Default.
timeout(Duration)Auto-dismiss after this duration. Default is 5 seconds.
no_timeout()Toast stays until manually dismissed.
dismissible(bool)Whether clicking the toast dismisses it. Default is true.
icon(impl Into<String>)Sets a custom icon glyph. If omitted, the variant picks a default (◔ for Default, ✓ for Success, ⚠ for Warning, ✗ for Error).
style(ToastStyle)Override the global style for this toast.
progress_value(f32)For Progress style, sets the fill percentage (0.0 to 1.0).
actions(&[&str])Adds inline action buttons (see Action style below).

The Toaster also has convenience methods that push pre-configured toasts:

extern crate tuile;
use tuile::prelude::*;
fn demo(toaster: &mut Toaster) {
toaster.info("Connection established");
toaster.success("File saved");
toaster.warning("Low disk space");
toaster.error("Network timeout");
}
fn main() {}

Each creates a toast with the named variant, a capitalized title, and your message.

ToastStyle

Ten styles, each with different visuals. The global style is set on the Toaster via .style(ToastStyle::X), or per-toast with toast.style(X).

StyleAppearance
CardRounded rectangle with shadow, icon on the left, title and message stacked. Default.
FlatFlat rectangle, no shadow, same layout as Card.
MinimalTransparent background, single-line text, no border.
PillHighly rounded ends, compact single-line layout.
OutlineTransparent background with a variant-colored border.
BannerFull-width bar (does not float in a corner), bold text.
GlassSemi-transparent fill, blurred effect (where terminals support it).
ProgressShows a horizontal progress bar under the message. Use .progress_value(f) to set fill. Does not auto-dismiss while progress is below 1.0.
ActionIncludes inline action buttons. Use .actions(&["Label", ...]) to add them.
GroupedStacks multiple toasts into a single card with a count badge when many arrive at once.

ToastPosition and ToastAnim

Toasts anchor to one corner or edge. Set the position on the Toaster:

extern crate tuile;
use tuile::widgets::{Toaster, ToastPosition};
fn demo() -> Toaster {
Toaster::new().position(ToastPosition::TopRight)
}
fn main() {}

Six positions: TopLeft, TopRight, BottomLeft, BottomRight, TopCenter, BottomCenter. Default is BottomRight. Center positions place toasts in a vertical stack at the top or bottom center of the screen.

Four animations:

AnimBehavior
SlideSlides in from the edge. Default.
FadeFades in opacity.
PopScales up from small to full size.
NoneAppears instantly, no animation.

Set the animation with .anim(ToastAnim::Slide). Animations run on entry and on close. The close animation is always a fade, regardless of the entry animation.

Timeouts and pause on hover

By default, toasts expire after their timeout (5 seconds) and start their close animation. Hovering over a toast does not pause the timer unless you enable it:

extern crate tuile;
use tuile::widgets::Toaster;
fn demo() -> Toaster {
Toaster::new().pause_on_hover(true)
}
fn main() {}

With pause on hover, the toast will not expire while the mouse is over it. The timer resumes when the mouse leaves.

Dismissing toasts

Click a dismissible toast to close it immediately, or call toaster.dismiss(id, now) from code. The toast plays its close animation and is removed after the fade completes (150ms by default). toaster.dismiss_all(now) starts the close animation for every toast.

The id comes from toast.id(), which returns a unique u64 assigned when the toast is created. Store the id when you push a toast if you need to dismiss it later:

extern crate tuile;
extern crate ratatui_core;
use tuile::prelude::*;
use std::time::Instant;
fn demo(toaster: &mut Toaster) {
let toast = Toast::new("Processing", "Please wait").no_timeout();
let id = toast.id();
toaster.push(toast);

// Later:
toaster.dismiss(id, Instant::now());
}
fn main() {}

Action style and inline buttons

The Action style adds clickable buttons inside the toast. Use .actions(&["Undo", "Dismiss"]) to provide the labels. The user navigates between buttons with Tab, Right, and Left, and activates the focused button with Enter or a click. When an action is activated, handle_key or handle_mouse returns Outcome::Changed.

To determine which action was activated, iterate toaster.toasts and check the focused_action field on toasts that have actions. The toasts vec is public, but focused_action is a private field, so you read it through the toast after the event. Typically you dismiss the toast after handling its action.

extern crate tuile;
extern crate ratatui_core;
use tuile::prelude::*;
use tuile::crossterm::event::{Event, KeyEvent};
use std::time::Instant;

fn handle_event(toaster: &mut Toaster, event: Event) {
let outcome = match event {
    Event::Key(k) => toaster.handle_key(k),
    Event::Mouse(m) => toaster.handle_mouse(m),
    _ => Outcome::Ignored,
};

if outcome == Outcome::Changed {
    // Check which toast's action was activated by inspecting toaster.toasts.
    // The actual focused_action field is private, so this is a simplified example.
    toaster.dismiss_all(Instant::now());
}
}
fn main() {}

Because focused_action is not exposed via a public accessor, you typically use the Action style when you know which toast has actions (for example, the most recently pushed undo toast) and dismiss it after any action.

Grouped stacking

The Grouped style condenses multiple toasts into a single card. When more than one toast with the same variant exists, the stack shows the newest toast’s message and a count badge. This prevents notification spam from filling the screen. Grouped toasts share the same timeout behavior as individual toasts.

Builder options

The Toaster itself has configuration methods:

MethodEffect
style(ToastStyle)Default style for all toasts. Default is Card.
position(ToastPosition)Where toasts anchor. Default is BottomRight.
anim(ToastAnim)Entry animation. Default is Slide.
max_visible(usize)Maximum toasts shown at once. Older toasts are hidden until newer ones close. Default is 3.
bar(Edge)Border thickness for toasts. Default is Edge::Thin.
pause_on_hover(bool)Whether hovering pauses the timeout. Default is false.
reduce_motion(bool)Disables animations. Default is false.

When not to use toasts

Toasts are for transient messages that do not require action or acknowledgment. Use an InlineAlert or Banner for errors that block progress, NotificationCenter for messages the user should review later, or a modal dialog for decisions that require a response.

NotificationCenter

A scrollable inbox for persistent notifications. Supports grouping by time or category, filtering by variant, marking as read, and dismissing individual items or the entire list.

State and entries

NotificationCenterState owns the notification list. Each Notification has a title, message, variant, time label, read status, source, and group. Push notifications with state.push(notification):

extern crate tuile;
use tuile::prelude::*;
use tuile::widgets::notify::{Notification, NotificationCenterState};
fn demo() {
let mut state = NotificationCenterState::new();
state.push(
    Notification::new("Deploy successful", "Production build deployed", "Today")
        .variant(Variant::Success)
        .time("2m ago")
);
state.push(
    Notification::new("High memory", "Container using 92% memory", "Today")
        .variant(Variant::Warning)
        .time("15m ago")
);
}
fn main() {}

The third argument to Notification::new is the group label (for example, “Today”, “Yesterday”, “Last week”). The notification center groups entries by this string and renders a divider between groups.

Reading and dismissing

state.unread_count() returns the number of unread notifications. state.mark_read(index) marks one as read. state.dismiss(index) removes a notification. state.dismiss_all() clears the entire list. Indexes refer to positions in the state.notifications vec.

The notification center handles keyboard and mouse input:

InputAction
Up, DownNavigate between notifications
EnterMark the selected notification as read
DeleteDismiss the selected notification
Mouse clickSelect and mark as read

The top bar has a “Clear all” button that calls dismiss_all().

Filtering

state.filter is an Option<Variant>. When set, only notifications matching that variant are shown. Set it to None to show all. The center renders a filter bar with buttons for each variant. Clicking a filter toggles it on and off.

Scrolling

The center scrolls to keep the selected item visible. Rendered height is determined by the area you pass to render. If the notification list exceeds the visible area, a scrollbar appears on the right.

A full-width announcement bar for important messages. Banners typically sit at the top of the screen and remain visible until dismissed.

extern crate tuile;
extern crate ratatui_core;
use tuile::prelude::*;
use tuile::widgets::notify::{Banner, BannerState, BannerStyle};

fn demo(area: Rect, buf: &mut Buffer) {
let mut state = BannerState::default();
Banner::new("Maintenance scheduled for tonight at 10 PM")
    .variant(Variant::Warning)
    .style(BannerStyle::Tinted)
    .action("Details")
    .render(area, buf, &mut state);
}
fn main() {}

BannerStyle

Three styles:

StyleAppearance
SolidSolid variant background, white text. High contrast.
TintedVariant color tinted 15% into the background with a left accent rail. Subtle.
OutlineTransparent background with a variant-colored border.

Options

MethodEffect
variant(Variant)Color. Default is Variant::Default.
style(BannerStyle)Visual style. Default is Solid.
action(impl Into<String>)Adds a clickable action button on the right.
closeable(bool)Whether the banner has a dismiss button. Default is true.

The BannerState tracks visibility and action clicks. It starts hidden. Call state.show() to reveal the banner. When the user dismisses it, state.visible() returns false. When the action button is clicked, handle_mouse returns Outcome::Changed and state.action_clicked() returns true for one frame.

InlineAlert

A stateless alert box for inline warnings, errors, or info messages. Two forms: framed (default) and compact.

extern crate tuile;
extern crate ratatui_core;
use tuile::prelude::*;
use tuile::widgets::notify::InlineAlert;

fn demo(area: Rect, buf: &mut Buffer) {
InlineAlert::new("Invalid input", "Password must be at least 8 characters")
    .variant(Variant::Error)
    .render(area, buf);
}
fn main() {}

The framed style draws a rounded border with an icon on the left, title in bold, and message below. The compact style removes the border and draws a single line with icon, title, and message inline.

MethodEffect
variant(Variant)Color. Default is Variant::Default.
dismissible(bool)Adds a close button (no state, purely visual). Default is false.
compact(bool)Switches to compact single-line layout. Default is false.

Because InlineAlert is stateless, dismissing it is the app’s job. If you want interactive dismissal, wrap it in your own widget with state.

count_badge

A helper function that draws a small circular badge with a count. Used by the notification center to show unread counts and by grouped toasts to show the stack size.

extern crate tuile;
extern crate ratatui_core;
use tuile::prelude::*;
use tuile::widgets::notify::count_badge;

fn demo(buf: &mut Buffer, theme: &Theme) {
count_badge(buf, 10, 2, 5, theme);
}
fn main() {}

The signature is count_badge(buf, x, y, count, theme). It draws a small circle at (x, y) with the count centered inside. Counts above 99 are rendered as “99+”. The badge uses the theme’s accent color.