TUI overview
urushi-tui-app is the starting point for a new full-screen application. You
describe the application’s state, the events it accepts, how those events
change state, and how the current state looks. The runtime owns the event loop,
drawing, and terminal session.
This model is The Elm Architecture (TEA). The name identifies a small set of responsibilities; you do not need to know Elm or TEA before using Urushi. The first-application tutorial introduces every part while building a complete keyboard-driven program.
Use it for applications whose interface remains active: dashboards, browsers, inspectors, monitors, editors, and other screens where input changes a model and the current model is redrawn.
See one application run
Section titled “See one application run”The counter below receives two key events. Each key immediately updates the Model, while slower background work returns later as Effect-completion Messages. Quitting restores the terminal and returns the final Model:
TEA in plain language
Section titled “TEA in plain language”A TEA application is a loop with one source of truth:
- The Model contains everything that can change the application’s behavior or appearance.
- A Message describes one event, such as a key press, timer tick, or file load result.
- update handles that message and changes the model.
- view reads the model and returns the next Urushi
View. - The runtime draws that View and waits for the next message.
update is the only place that mutates the model. view does not perform work
or write to the terminal; it only describes what the current model should look
like. One-shot work is returned as an Effect, and ongoing event sources are
declared as Subscriptions. Their results re-enter the same loop as messages.
This means a key does not modify a widget directly. It becomes a message, the message changes the model, and the model produces a new View. That one-way path is what keeps application behavior testable without a live terminal.
Boxes are values. Labels on arrows are functions or runtime actions. The runtime draws each View, then delivers the next Message.
In the recording, key updates belongs to the Model. The runtime turns each
keypress into a Message, calls update, calls view with the changed Model,
and draws the returned View. The delayed job does not mutate the Model from a
worker thread; its result returns through the same Message entrance. That is
TEA in Urushi.
Build your first application without prior TEA knowledge →
Choose who owns the loop
Section titled “Choose who owns the loop”| Starting point | Choose | You own | Urushi owns |
|---|---|---|---|
| Build a new full-screen application | urushi-tui-app |
Domain model, messages, update rules, and View | Input sources, effects, ordered delivery, drawing, and terminal restoration |
| Drive a synchronous terminal loop yourself | urushi-tui |
Session, events, resize decisions, timing, and shutdown | Screen buffers, cell diffing, transactional commit, and failed-draw recovery |
| Add Views to an existing Ratatui application | urushi-adapter-ratatui |
Ratatui terminal, event loop, state, scheduling, and graphics lifecycle | Stateless View resolution and writes into the supplied Ratatui buffer |
These are ownership choices, not progressively more advanced layers. Start with the row matching the loop the application already has. The recording and first-application tutorial use the first row.
What the framework covers
Section titled “What the framework covers”- A testable application model.
init,update,view, andsubscriptionsseparate state transitions from terminal I/O. The application model contains no Ratatui backend types. - Terminal and external events as messages. Applications can subscribe to keys, paste, focus, mouse input, surface changes, intervals, process signals, terminal draw errors, and application-defined streams or producers.
- Work outside
update. Effects cover blocking work, futures, delayed messages, latest-only replaceable work, concurrent batches, and orderly shutdown. Every completion returns through the message path. - Full-screen presentation.
viewreturns the same styled, layout-aware UrushiViewused by CLI output. The runtime resolves it against the current surface and writes a frame of terminal cells. - Runtime ownership. The runtime orders accepted messages, reconciles subscriptions, coalesces physical draws without dropping model transitions, and restores the terminal session on normal and error returns. During panic unwinding its session guard makes a final best-effort restoration attempt.
- A production entry point.
urushi_tui_app::run(app)supplies the default executor, clock, terminal connection, presentation, and session profile.Runtime::new(app)makes the physical backend, executor, clock, and session options configurable; presentation remains the concreteurushi-tuiscreen.
Focus movement, modal stacks, key maps, commands, and navigation rules remain ordinary application state and messages. Urushi provides the application and presentation machinery; it does not prescribe those product semantics.
From input to a frame
Section titled “From input to a frame”The View is the same renderer-neutral tree used by the rest of Urushi. A
full-screen application does not need a second layout language, and its model
does not contain Ratatui or another backend’s types.
Use Layout for ordinary screen structure, components for lists, tables, trees, and scrollbars, and Canvas for positioned or overlapping drawing. To place Kitty or Sixel images in those layouts, add terminal graphics and let the runtime own their frame lifecycle.
A view method can use core layout and theme values directly:
use urushi::{BlockStyle, Border, TextStyle, View};
fn view(model: &Model) -> View { View::block( BlockStyle::new() .border(Border::ROUNDED) .padding((0, 1)), View::text(model.status.clone(), TextStyle::new()), )}For status == "Ready", that region resolves to:
╭───────╮│ Ready │╰───────╯The application framework
Section titled “The application framework”An Application provides four operations:
initcreates the initial model and effect;updateapplies one message and returns the next effect;viewprojects the current model into an UrushiView; andsubscriptionsdeclares the event sources active for the current model.
Effects describe work outside the model. Subscriptions describe ongoing input, surface, timer, signal, or application-defined sources. Their results return as messages and pass through the same ordered update path.
Build your first application →
Choose effects, subscriptions, and backpressure →
Understand message ordering and draw scheduling →
The native frame engine
Section titled “The native frame engine”urushi-tui exposes a concrete Screen and a Frame borrowed for one draw.
The screen owns working and committed Urushi cell buffers, computes their diff,
writes terminal commands, and adopts the new frame only after every output step
succeeds. A caller can drive it synchronously without the application runtime.
Application remains written against Urushi types, not a presentation backend.
The runtime resolves its View and writes the resulting graphemes through the
same Frame API.
Build a caller-owned loop with Screen and Frame →
Ratatui is an optional adapter
Section titled “Ratatui is an optional adapter”urushi-adapter-ratatui supplies ViewWidget and related adapters for an
application that already owns a Ratatui loop and buffer. Neither urushi-tui
nor urushi-tui-app depends on Ratatui.
Use Urushi inside an existing Ratatui application →
Crate boundaries
Section titled “Crate boundaries”The three independently useful full-screen surfaces have separate package boundaries:
urushi-tuiowns a synchronousScreen, draw-scopedFrame, cell buffers, diffing, and transactional output;urushi-tui-appownsApplication,Runtime, effects, subscriptions, delivery, scheduling, input, and session ownership; andurushi-adapter-ratatuiowns stateless conversion into a caller-owned Ratatui buffer.
The low-level frame engine is a concrete Urushi Screen, not a replaceable
Ratatui-backed terminal, and Ratatui is not a runtime dependency. Choose only
the package whose ownership model the application needs.