ScriptsApr 13, 2026·3 min read

Ratatui — Terminal User Interface Library for Rust

Ratatui is a Rust library for building rich terminal user interfaces (TUIs). It provides widgets for tables, charts, lists, paragraphs, tabs, and more — enabling you to build beautiful, interactive terminal applications with immediate-mode rendering.

SC
Script Depot · Community
Quick Use

Use it first, then decide how deep to go

This block should tell both the user and the agent what to copy, install, and apply first.

# Add Ratatui to your Rust project
cargo add ratatui crossterm
use ratatui::{
    crossterm::event::{self, Event, KeyCode},
    layout::{Constraint, Layout},
    style::{Color, Style},
    widgets::{Block, Borders, Paragraph},
    DefaultTerminal,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut terminal = ratatui::init();
    loop {
        terminal.draw(|frame| {
            let area = frame.area();
            let block = Block::default()
                .title(" My App ")
                .borders(Borders::ALL)
                .style(Style::default().fg(Color::Cyan));
            let paragraph = Paragraph::new("Hello, Ratatui!")
                .block(block);
            frame.render_widget(paragraph, area);
        })?;
        if matches!(event::read()?, Event::Key(k) if k.code == KeyCode::Char('q')) {
            break;
        }
    }
    ratatui::restore();
    Ok(())
}

Introduction

Ratatui is a community fork of tui-rs that has become the standard library for building terminal UIs in Rust. It uses an immediate-mode rendering approach — you describe what the UI should look like each frame, and Ratatui efficiently diffs and updates only what changed. This makes building complex, interactive TUIs surprisingly straightforward.

With over 20,000 GitHub stars, Ratatui powers popular tools like gitui, bottom, kdash, and many other terminal applications. It is the Rust equivalent of Bubble Tea for Go.

What Ratatui Does

Ratatui provides a layout system (constraints-based, like CSS Flexbox) and a rich set of widgets (paragraphs, lists, tables, charts, gauges, tabs, etc.). You compose layouts and widgets each frame, and Ratatui handles terminal manipulation, color rendering, and efficient screen updates via crossterm or termion backends.

Architecture Overview

[Your Application]
State + event handling
        |
   [Ratatui]
   Layout + Widgets
        |
+-------+-------+
|       |       |
[Layout]  [Widgets]
Constraint  Paragraph
Direction   List, Table
Rect        Chart, Gauge
Margin      Tabs, Block
        |
   [Terminal Backend]
   crossterm (cross-platform)
   or termion (Unix)
        |
   [Terminal Emulator]
   Renders ANSI escape codes

Self-Hosting & Configuration

use ratatui::{
    layout::{Constraint, Direction, Layout},
    style::{Color, Modifier, Style},
    widgets::{Block, Borders, List, ListItem, Paragraph, Gauge},
    DefaultTerminal, Frame,
};

struct App {
    items: Vec<String>,
    selected: usize,
    progress: f64,
}

fn ui(frame: &mut Frame, app: &App) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(3),
            Constraint::Min(5),
            Constraint::Length(3),
        ])
        .split(frame.area());

    // Title
    let title = Paragraph::new("Dashboard")
        .style(Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD))
        .block(Block::default().borders(Borders::ALL));
    frame.render_widget(title, chunks[0]);

    // List
    let items: Vec<ListItem> = app.items.iter()
        .enumerate()
        .map(|(i, item)| {
            let style = if i == app.selected {
                Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)
            } else {
                Style::default()
            };
            ListItem::new(item.as_str()).style(style)
        })
        .collect();
    let list = List::new(items)
        .block(Block::default().title(" Items ").borders(Borders::ALL));
    frame.render_widget(list, chunks[1]);

    // Progress bar
    let gauge = Gauge::default()
        .block(Block::default().title(" Progress ").borders(Borders::ALL))
        .gauge_style(Style::default().fg(Color::Green))
        .ratio(app.progress);
    frame.render_widget(gauge, chunks[2]);
}

Key Features

  • Rich Widgets — Paragraph, List, Table, Chart, Gauge, Tabs, Sparkline
  • Layout System — constraint-based layouts (like Flexbox)
  • Styling — colors, bold, italic, underline, and 256/RGB colors
  • Immediate Mode — describe UI each frame, Ratatui handles diffing
  • Backend Agnostic — crossterm (cross-platform) or termion (Unix)
  • Composable — nest layouts and widgets freely
  • Scrolling — built-in scroll state for lists, tables, and text
  • Custom Widgets — implement the Widget trait for custom components

Comparison with Similar Tools

Feature Ratatui Bubble Tea (Go) Textual (Python) ncurses Ink (React)
Language Rust Go Python C JavaScript
Paradigm Immediate mode Elm architecture Widget tree Procedural React components
Widgets Rich built-in Bubbletea + Bubbles Rich built-in Low-level React-style
Cross-Platform Yes (crossterm) Yes Yes Unix-focused Yes
Performance Excellent Excellent Good Excellent Moderate
Best For Rust TUIs Go TUIs Python TUIs C/C++ TUIs JS TUIs

FAQ

Q: Ratatui vs tui-rs — what happened? A: Ratatui is a community fork of tui-rs after the original maintainer stepped back. Ratatui is actively maintained with frequent releases and new features. Always use Ratatui for new projects.

Q: How does immediate-mode rendering work? A: Each frame, you describe the entire UI from scratch. Ratatui compares the new frame with the previous one and only sends the differences to the terminal. This makes UI code simple — no manual state tracking for what changed.

Q: Can I use Ratatui for production tools? A: Yes. Tools like gitui (Git TUI), bottom (system monitor), and kdash (Kubernetes dashboard) use Ratatui in production with thousands of users.

Q: How do I handle user input? A: Use crossterm events (event::read()) in your main loop. Match on KeyCode, MouseEvent, or Resize. Ratatui does not handle input — it only renders.

Sources

Discussion

Sign in to join the discussion.
No comments yet. Be the first to share your thoughts.

Related Assets