Skip to content

Ratatui

Developer UtilitiesCLI/UtilsRust

What it is

Ratatui builds rich terminal user interfaces in Rust with an immediate-mode API, layout system and a library of widgets.

The entire UI is redrawn each frame from current state. Layouts split the area into constrained regions, and widgets render into them.

Installation

cargo add ratatui crossterm

Getting started

The smallest useful thing you can do with it, and what each part means.

Layout and widgets
use ratatui::{prelude::*, widgets::*};

fn draw(frame: &mut Frame, app: &App) {
    let chunks = Layout::vertical([
        Constraint::Length(3),      // fixed header
        Constraint::Min(0),         // body takes the rest
        Constraint::Length(1),      // status line
    ])
    .split(frame.area());

    frame.render_widget(
        Paragraph::new("Library").block(Block::bordered()),
        chunks[0],
    );

    let items: Vec<ListItem> = app.books.iter()
        .map(|b| ListItem::new(b.title.as_str()))
        .collect();

    frame.render_stateful_widget(
        List::new(items)
            .block(Block::bordered().title("Books"))
            .highlight_symbol("> "),
        chunks[1],
        &mut app.list_state,   // holds the selection
    );
}
The whole frame is rebuilt every draw, so there is no incremental update logic to get wrong. Selection lives in a separate state object that survives between frames.

Advanced usage

Where the library earns its place over a simpler alternative.

Terminal setup and guaranteed restore
fn main() -> Result<()> {
    enable_raw_mode()?;
    execute!(stdout(), EnterAlternateScreen)?;
    let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;

    // Restore the terminal even if the app panics — otherwise the user is
    // left with an unusable shell.
    let original = std::panic::take_hook();
    std::panic::set_hook(Box::new(move |info| {
        let _ = disable_raw_mode();
        let _ = execute!(stdout(), LeaveAlternateScreen);
        original(info);
    }));

    let result = run(&mut terminal);

    disable_raw_mode()?;
    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
    result
}
The panic hook is not optional in practice. Without it, any panic leaves the terminal in raw mode with no echo, and the user has to run `reset` blind.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

The terminal is broken after exit
Raw mode was not disabled. Add the panic hook and ensure cleanup runs on every path.
Rendering flickers
Drawing more often than necessary. Redraw on events and a timer rather than in a tight loop.

Best practices

  • Install a panic hook that restores the terminal; a crash otherwise leaves the shell unusable.
  • Keep draw a pure function of state — no I/O inside the render path.
  • Poll for events with a timeout so the UI redraws on a predictable cadence.
  • Use stateful widgets for lists and tables so selection survives redraws.

Background

Why it exists, and what it was reacting to.

Ratatui is the community-maintained continuation of tui-rs after that project was archived. It powers a generation of Rust terminal tools including gitui and bottom.