ratatui-kit 0.10.3

A framework for building interactive terminal user interfaces with ratatui
Documentation

Ratatui Kit

Build component-driven terminal UIs in Rust with React-style components, hooks, props, routing, input layers, theming, and global state. Powered by Ratatui and Tokio.

Ask DeepWiki crates.io Downloads docs.rs Website License: MIT

Documentation · Quick Start · Components · Examples · GitHub · Ecosystem


Overview

Ratatui Kit is a component framework for terminal UIs built on top of Ratatui. It brings familiar frontend ideas - components, props, hooks, context, routing, theming, and scoped global state - into Rust terminal applications without hiding the underlying Ratatui drawing model.

If you know React, the mental model should feel familiar:

  • element! gives you JSX-like declarative UI syntax.
  • #[component] turns a function into a reusable component.
  • use_state, use_future, use_async_state, use_effect, and use_context organize state and side effects.
  • RouterProvider, Outlet, and routes! model multi-page terminal apps.
  • Atom and use_atom provide process-wide reactive state behind the atom feature.

Ratatui gives you the terminal canvas and widgets. Ratatui Kit adds component identity, state retention, reconciliation, input routing, theming, and async-aware rendering.


Features

  • Declarative components: write terminal UI trees with element!, including first-class if, if let, for, and match control flow inside child blocks.
  • React-style hooks: use local state, futures, effects, memoized values, context, terminal size, lifecycle cleanup, and input handlers in component functions.
  • State retention by identity: the runtime reuses component instances across frames using ElementKey + TypeId, preserving hook slots and local state when identity stays stable.
  • Waker-driven rendering: state writes wake the render loop instead of requiring manual redraw calls.
  • Async-native runtime: the terminal loop runs on Tokio, so components can spawn futures and react to async work naturally.
  • Flex-style layout: LayoutStyle maps common layout concepts (flex_direction, justify_content, gap, margin, offset, width, height) to Ratatui layout primitives.
  • Unified theming: a shared Palette is the single color source, and every component derives its styles from it via a per-component FooTheme. Recolor globally with PaletteProvider, override one component type with ThemeOverride, or drive the palette from an Atom to re-theme at runtime.
  • Central input routing: InputRuntime, InputLayer, EventScope, EventPriority, and EventResult make modals and edit modes block background shortcuts cleanly.
  • Local and global state: use component-local State<T> for local lifetimes and Atom<T> for process-wide shared state.
  • Built-in router: RouterProvider, Outlet, routes!, use_navigate, use_route, and use_params are available behind the router feature.
  • Native widget escape hatch: use widget(expr) and stateful(widget, state) to embed existing Ratatui widgets directly.
  • Small default dependency surface: the default feature set is empty; opt into router, atom, input, tree, table, virtual-list, serde, or full as needed. The theming protocol is always-on with no extra dependency.

Quick start

Install the crate:

cargo add ratatui-kit

Or let Cargo add the full feature set:

cargo add ratatui-kit --features full

This writes a dependency entry like:

[dependencies]
ratatui-kit = { version = "...", features = ["full"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }

The default feature set is intentionally empty. Enable only the capabilities you need, or use full for examples and prototypes.

Counter example

use ratatui_kit::{
    crossterm::event::{Event, KeyCode, KeyEventKind},
    prelude::*,
    ratatui::{
        layout::{Constraint, Direction, Flex},
        style::{Style, Stylize},
        text::Line,
    },
};

#[tokio::main]
async fn main() {
    element!(Counter)
        .fullscreen()
        .await
        .expect("failed to run the application");
}

#[component]
fn Counter(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
    let mut count = hooks.use_state(|| 0_u64);

    hooks.use_future(async move {
        loop {
            tokio::time::sleep(std::time::Duration::from_secs(1)).await;
            count += 1;
        }
    });

    let mut exit = hooks.use_exit();
    hooks.use_event_handler(EventScope::Current, EventPriority::Normal, move |event| {
        let Event::Key(key) = event else {
            return EventResult::Ignored;
        };
        if key.kind == KeyEventKind::Press
            && matches!(key.code, KeyCode::Char('q') | KeyCode::Char('Q'))
        {
            exit();
            return EventResult::Consumed;
        }
        EventResult::Ignored
    });

    element!(
        Center(width: Constraint::Length(48), height: Constraint::Length(9)) {
            Border(
                flex_direction: Direction::Vertical,
                justify_content: Flex::Center,
                border_style: Style::new().cyan(),
                top_title: Line::from(" ratatui-kit counter ").cyan().bold().centered(),
                bottom_title: Line::from(" q quit | Ctrl+C exit ").dark_gray().centered(),
            ) {
                Text(text: Line::styled(
                    format!("Counter: {:02}", count.get()),
                    Style::new().green().bold(),
                ).centered())
            }
        }
    )
}

Theming

Every built-in component reads its colors from a shared Palette; there is no hardcoded color. Tune the palette in one place and the whole tree re-colors:

use ratatui_kit::prelude::*;
use ratatui_kit::ratatui::style::Color;

let mut palette = Palette::default();
palette.accent = Color::Rgb(94, 175, 255);

// every component inside derives its styles from `palette`
let _themed = element!(PaletteProvider(palette: palette) {
    Text(text: "themed by the shared palette")
});

Per-call style props are Option<Style> (None = theme, Some(s) = patch over the theme, Some(Style::reset()) = clear). Re-style one component type with ThemeOverride::<BorderTheme>(theme: ...), and drive the palette from an Atom<Palette> to switch themes at runtime. See the Theming guide.


AI-assisted development

Ratatui Kit ships an AI agent skill — a packaged knowledge base that teaches your AI coding assistant the framework's real components, props, and hooks, the element! macro, input layers, theming, and the router — so you can ask it to "build me a terminal todo app" and get code that compiles and follows the framework's idioms, instead of guessed APIs.

npx skills add yexiyue/ratatui-kit --skill ratatui-kit

The skill lives in skills/ratatui-kit/ and your assistant consults it automatically. Pair it with the general-purpose rust-best-practices and rust-async-patterns skills for Rust-level correctness. See AI-assisted development for the full guide.


Built-in components and hooks

This overview is intentionally compact. See the documentation site and docs.rs for signatures and deeper examples.

Components

Component Purpose Feature
View, Border, Center, Fragment Layout and container primitives core
Text, WrappedText Text rendering and measured wrapping core
Positioned Absolute positioning core
Modal, ConfirmModal, AlertModal, ShortcutInfoModal Modal surfaces with input isolation core
Select, MultiSelect Single and multiple selection lists core
ScrollView Scrollable viewport core
ContextProvider Scoped context injection core
PaletteProvider, ThemeOverride Theme injection — global palette and per-component overrides core
Input, SearchInput Single-line input and search input input
TreeSelect Tree selection tree
Table Data-driven table with cell-grid borders, wrapping, responsive columns, footer rows, and row/column highlighting table
VirtualList Virtualized list rendering virtual-list
RouterProvider, Outlet Routing container and nested route outlet router

You can also bridge any native Ratatui widget with widget(expr) or stateful(widget, state).

Hooks

Hook Purpose Feature
use_state Component-local reactive state core
use_future, use_async_state Async tasks and async state core
use_memo, use_effect Memoized derived values and side effects core
use_context Read values from the nearest context provider core
use_palette, use_component_theme Read the current palette or a resolved component theme core
use_event_handler Register scoped input handlers core
use_input_layer Create a same-frame input layer handle core
use_insert_before, use_terminal_size Insert content before render and read terminal size core
use_exit, use_on_drop Exit the application and run cleanup callbacks core
use_navigate, use_route, use_params Router navigation and route data router
use_atom Subscribe to global atoms atom

Procedural macros

element! · #[component] · #[derive(Props)] · routes! (router) · #[with_layout_style]


Feature flags

Feature Enables Extra dependencies
default Nothing ([]) -
router RouterProvider, Outlet, routes!, use_navigate, use_route, use_params regex
atom Atom, AtomState, use_atom -
input Input, SearchInput, and the tui_input re-export tui-input
tree TreeSelect and the tui_tree_widget re-export tui-tree-widget
table Table, width-aware wrapping, responsive columns, and grid borders unicode-width
virtual-list VirtualList and the tui_widget_list re-export tui-widget-list
serde Serialize / Deserialize for Palette serde, ratatui/serde
full All optional features above -

The theming protocol (Palette, ComponentTheme, PaletteProvider, ThemeOverride, and every FooTheme) is always-on and needs no feature flag. The textarea feature is currently disabled during the Ratatui 0.30 migration because tui-textarea does not yet provide a compatible release.


Documentation and examples

The GitHub repository carries runnable examples — clone it and cargo run --example <name> (e.g. counter, atom_state, router, modal, table, theme, todo_app); the workspace enables full.


Ecosystem

Beyond the built-in components, ratatui-kit has a growing ecosystem of third-party component crates, published independently as ratatui-kit-<name>. They depend only on the stable Extension API and do not need to be merged into this repo, so the core stays small.

Design goals

Ratatui Kit is inspired by React, iocraft, and ink, but stays close to Rust and Ratatui:

  • Declarative: describe what the UI should look like instead of mutating terminal buffers by hand.
  • Reactive: state changes wake the runtime, and the framework reconciles the component tree for the next frame.
  • Async-first: timers, IO, and background tasks fit into component lifetimes through Tokio.
  • Composable: the built-in components stay business-neutral; application-specific behavior belongs in your own hooks, providers, and components.
  • Escape-friendly: when a native Ratatui widget is the right tool, embed it directly.

Contributing

Issues and pull requests are welcome on GitHub. Before sending a PR, run the CI validation matrix (cargo fmt --all --check, cargo clippy --all-targets --all-features --workspace -- -D warnings, cargo test --locked --all-features --workspace --lib --tests --examples).

License

Ratatui Kit is released under the MIT License.