cloudparts

Ratatui

A Rust library for terminal user interfaces that redraws the whole screen from your app’s state on every frame, then writes only the cells that changed.

Type
Open source
Made by
The Ratatui Developers (forked from tui-rs by Florian Dehau)
License
MIT
Facts checked
13 October 2026

What it is

Ratatui draws in immediate mode. Your program owns its state and its loop; each time it wants to update the screen it calls terminal.draw() with a closure, and inside that closure it renders every widget again into a Buffer, a grid of Cells that each hold one symbol and its style. Widgets are plain values that implement a render() method and are thrown away after the frame. When the closure returns, Ratatui compares the new buffer with the previous one and writes only the difference to the terminal, then swaps the two buffers. Layout is computed from constraints such as Length(3) or Fill(1) that split a Rect into smaller areas.

Ratatui only renders. Reading keys and mouse events, raw mode and the alternate screen come from a backend library: Crossterm by default, or Termion, Termwiz or Termina, plus a TestBackend for unit tests. Since version 0.30 (December 2025) the project is a workspace of crates: ratatui-core holds the traits and types and changes slowly, so third-party widget crates can depend on it, while ratatui-widgets and one crate per backend sit on top. With default features off, it compiles for no_std targets; the team reports running it on ESP32 and STM32H7 microcontrollers.

The project is a community fork of tui-rs, which Florian Dehau wrote in 2016 and stopped maintaining. Ratatui’s first release, 0.20.0, came out in March 2023, and tui-rs now points its users to it. It is still released as 0.x and is actively developed: 0.30.2 shipped in June 2026, and commits landed in October 2026. OpenAI’s Codex CLI draws its terminal interface with it.

Why teams pick it

  • No widget tree to keep in sync: the UI is a function of your state, which sidesteps most of the lifetime and ownership fights a retained widget tree causes in Rust
  • Double buffering with a diff means each frame writes only changed cells to the terminal, however much the app redraws
  • Does not take over your program: there is no app.run() event loop, so it can be added to an existing program, even just to chart its internal state
  • Pluggable backends (Crossterm, Termion, Termwiz, Termina) and no_std support reach from desktop terminals to microcontrollers, and TestBackend lets you unit test the UI without a terminal

Watch out for

  • Still 0.x: minor releases carry breaking changes (0.30 removed block::Title, renamed Alignment and changed the Backend trait), listed in a separate BREAKING-CHANGES.md
  • You write the event loop, input handling and app structure yourself; if the loop blocks, the screen stops updating
  • Widgets keep nothing between frames, so a selection or scroll offset lives in a separate state value (such as ListState for a StatefulWidget) that your app stores and passes in each time
  • Needs a recent compiler: the minimum supported Rust version is 1.88 as of 0.30.1

Alternatives

What all terminal user interfaces (tuis) have in common →Compare side by side →

How it fits with other technologies

A gauge that grows with the right arrow key (Ratatui 0.30) · rust

use ratatui::crossterm::event::{self, Event, KeyCode};
use ratatui::layout::{Constraint, Layout};
use ratatui::widgets::{Block, Gauge, Paragraph};
use ratatui::Frame;

fn main() -> std::io::Result<()> {
    let mut percent: u16 = 0;
    // run() enters raw mode and the alternate screen, and restores them on exit
    ratatui::run(|terminal| loop {
        terminal.draw(|frame| render(frame, percent))?;
        if let Event::Key(key) = event::read()? {
            match key.code {
                KeyCode::Char('q') => break Ok(()),
                KeyCode::Right => percent = (percent + 10).min(100),
                _ => {}
            }
        }
    })
}

// Called for every frame: describe the whole screen from the current state.
fn render(frame: &mut Frame, percent: u16) {
    let [top, rest] = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)])
        .areas(frame.area());
    let gauge = Gauge::default().block(Block::bordered().title("Upload")).percent(percent);
    frame.render_widget(gauge, top);
    frame.render_widget(Paragraph::new("Right arrow adds 10%, q quits"), rest);
}

See it in a real stack

No architecture uses Ratatui yet. Drop it into the playground and wire it up yourself.Open playground →

Worth exploring

WebsiteAwesome TUIsJustin Garrison and contributors, on GitHubA community-maintained list of more than 700 terminal apps and libraries in 13 sections, from dashboards, editors and Docker or Kubernetes front ends to games, plus TUI libraries grouped by language (Python, Go, C, C++, Java, .NET, Rust and others). Entries must be interactive, maintained and not just wrappers around tools like fzf; they arrive by pull request with one-line descriptions, and the maintainer says to run them at your own risk.
All resources →
Where it came from2016–2026

Florian Dehau started tui-rs in 2016 as a Rust library for terminal dashboards, inspired by the JavaScript library blessed-contrib and the Go library termui. It rendered in immediate mode with intermediate buffers and left input handling to libraries such as Crossterm and Termion, and its popularity kept growing as people used it in more ambitious projects.

In August 2022 Dehau wrote that their work left little energy for the crate and asked for people to take it over, listing its gaps: complex layouts, scrolling and mouse support were hard, and it only suited full-screen apps. Contributors started a community fork in early 2023, moved the open pull requests over and released it as Ratatui 0.20.0 in March 2023, a maintenance release with bug fixes and no big features. In August 2023 tui-rs was marked unmaintained and its README pointed to Ratatui.

The fork kept the immediate-mode design and grew around it: a website with tutorials and templates, convenience functions such as ratatui::init() (0.28.1) and ratatui::run() (0.30), and in December 2025 a split into a workspace of crates with no_std support for embedded targets.

Why it’s built this way

Immediate mode with intermediate buffers
Rebuilding widgets from state each frame avoids the lifetime and ownership problems that retained widget trees cause in Rust, which Dehau saw as one reason beginners liked the crate; the buffer diff keeps the terminal output small.
A library, not a framework
tui-rs never had an app.run() that takes over execution, so it could be dropped into any program, even just to visualize a program’s internal state. Ratatui kept that: input comes from the backend, and the loop is yours.
Fork instead of waiting
The original maintainer had stepped back and changes were waiting unreleased. A community fork under a new name let new maintainers transfer the open pull requests from tui-rs and ship them, starting with a fixes-only release.
Split into a workspace of crates in 0.30
A slower-moving ratatui-core gives widget authors a more stable API, and separate widget and backend crates cut compile times and let apps pick their dependencies.

Timeline

  1. 2016

    Florian Dehau creates tui-rs

  2. 2022

    Dehau asks the community to take over maintenance (August)

  3. 2023

    Community fork released as Ratatui 0.20.0 (March); tui-rs marked unmaintained (August)

  4. 2025

    Ratatui 0.30 splits into crates and adds no_std support (December)

  5. 2026

    Ratatui 0.30.2 (June); development continues on 0.31

Sources11 · facts checked 13 October 2026

Improve this page on GitHub