Toast
Transient notifications stacked in a corner. Toast is one message, ToasterState is the stack your app keeps, and ToasterWidget draws it.
use ratcn::{Toast, ToasterState, ToasterWidget};
// In update(), when something happens:
state.toasts.push(Toast::success("Saved"), now);
// In draw(), after everything else, so toasts sit on top:
frame.render_widget(
ToasterWidget::new(&state.toasts, now).themed(&theme),
frame.area(),
);Toasts take no focus and handle no events. They are paint-only, so they work in a plain Ratatui app with no Ratcn runtime.
Kinds
The kind sets the accent color and icon. Each has a shorthand constructor, or pass one to .kind(...).
| Kind | Shorthand | Use for |
|---|---|---|
Default | Toast::new | Neutral news. |
Success | Toast::success | Something worked. |
Error | Toast::error | Something failed. Consider .persistent(). |
Warning | Toast::warning | Needs attention, but did not fail. |
Info | Toast::info | Neutral information, accented to stand out. |
Loading | Toast::loading | Work in progress. Usually persistent. |
Toast::error("Upload failed")
.description("Check your connection and try again.")
.persistent().description(...) adds a second line under the title, and .border(false) drops the border on one toast.
Your app owns the clock
Ratcn never calls Instant::now. Every method that cares about time takes a Duration from you, which is what lets toasts work in the browser and be tested without sleeping.
The loop is three steps:
// 1. Push with the current reading from your clock.
state.toasts.push(Toast::success("Saved"), now);
// 2. Ask when to wake up next, and wait that long.
match state.toasts.time_until_next_expiry(now) {
Some(timeout) => poll_for_input(timeout)?,
None => wait_for_input()?, // nothing expires; block until input
}
// 3. When the timer fires, drop what expired and redraw if anything changed.
if state.toasts.prune_expired(now) {
redraw();
}Skip step 2 and nothing ever disappears — the stack only changes when you tell it time has passed. In a browser loop that redraws continuously, pruning once per frame is enough.
Toasts expire after 4 seconds by default. .duration(...) changes that and .persistent() disables it.
ToasterState can prune expired entries or clear the entire stack, including persistent toasts. It does not dismiss or replace one entry by identity. If an app needs individual lifecycle control, it can own a custom collection of ToastEntry values and render that collection with ToasterWidget::from_entries(...).
Placement
.position(...) picks the corner or edge; the stack grows away from it, so the newest toast is always nearest. .width(...), .gap(...), and .offset(x, y) size and inset the stack, and .visible_toasts(n) caps how many show at once — older ones stay in the state and still expire on schedule.
use ratcn::ToastPosition;
ToasterWidget::new(&state.toasts, now)
.themed(&theme)
.position(ToastPosition::TopRight)
.visible_toasts(5)Positions are TopLeft, TopCenter, TopRight, BottomLeft, BottomCenter, and BottomRight. ToastPosition::is_top() reports which way a stack grows, if your own layout needs to know.
Sizing
Toasts have no fixed height — the title and description wrap at the stack width and the widget measures the result. If the area cannot hold every visible toast, the newest that fit whole are drawn and the rest wait for the next frame. A toast is never clipped mid-content and toasts never overlap.
Styling
.themed(&theme) derives every color. .style(ToasterStyle) takes explicit ones — one surface and border shared by all toasts, plus an accent per kind.
use ratcn::ToasterStyle;
let mut style = ToasterStyle::from_theme(&theme);
style.error = theme.accent;
ToasterWidget::new(&state.toasts, now).style(style)Notes
ToasterState::clear()dismisses everything, including persistent toasts;len()andis_empty()report the stack size, useful for reserving space.ToastandToastEntryexpose read accessors (title,description_text,toast_kind,has_border,created_at,age,is_expired) for writing your own renderer againstentries().from_entries(...)renders a bareToastEntryslice, for apps that keep toasts in their own structure for individual lifecycle control..visible()iterates what would be drawn.ToasterStyle::fallback()is the no-theme palette, for plain ANSI terminals.- The widget skips expired toasts while drawing, so a late prune never shows a stale one.
Full API
Every method, with parameter and edge-case detail: Toast, ToastKind, ToasterState, ToastEntry, ToasterWidget, ToastPosition, ToasterStyle.
See Also
Loop and backend wiring, including the browser case: Host Integration.