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.
Documentation · Quick Start · Components · Examples · Ecosystem · 简体中文
Ratatui Kit is a component framework for terminal UIs built on top of Ratatui. It brings familiar frontend ideas - components, props, hooks, context, routing, 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, anduse_contextorganize state and side effects.RouterProvider,Outlet, androutes!model multi-page terminal apps.Atomanduse_atomprovide process-wide reactive state behind theatomfeature.
Ratatui gives you the terminal canvas and widgets. Ratatui Kit adds component identity, state retention, reconciliation, input routing, and async-aware rendering.
Table of contents
- Declarative components: write terminal UI trees with
element!, including first-classif,if let,for, andmatchcontrol 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:
LayoutStylemaps common layout concepts (flex_direction,justify_content,gap,margin,offset,width,height) to Ratatui layout primitives. - Unified theming: a shared
Paletteis the single color source, and every component derives its styles from it via a per-componentFooTheme. Recolor globally withPaletteProvider, override one component type withThemeOverride, or drive the palette from anAtomto re-theme at runtime. - Central input routing:
InputRuntime,InputLayer,EventScope,EventPriority, andEventResultmake modals and edit modes block background shortcuts cleanly. - Local and global state: use component-local
State<T>for local lifetimes andAtom<T>for process-wide shared state. - Built-in router:
RouterProvider,Outlet,routes!,use_navigate,use_route, anduse_paramsare available behind therouterfeature. - Native widget escape hatch: use
widget(expr)andstateful(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, orfullas needed. The theming protocol is always-on with no extra dependency.
Install the crate:
cargo add ratatui-kitOr let Cargo add the full feature set:
cargo add ratatui-kit --features fullThis 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.
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())
}
}
)
}Run the example from this repository:
cargo run --example counterRatatui 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, 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-kitThe 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.
README keeps the API overview intentionally compact. See the documentation site and docs.rs for signatures and deeper examples.
| 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).
| 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 |
element! · #[component] · #[derive(Props)] · routes! (router) · #[with_layout_style]
| 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 textarea feature is currently disabled during the Ratatui 0.30 migration because tui-textarea does not yet provide a compatible release.
- Learning path
- Quick start
- Installation and feature flags
- Hooks
- State model
- Theming
- Routing
- Built-in components
- Examples
- Simplified Chinese docs
- DeepWiki
Selected runnable examples:
cargo run --example counter # local state + async updates
cargo run --example atom_state # global atom state
cargo run --example router # RouterProvider and nested Outlet
cargo run --example modal # modal input isolation
cargo run --example table # data table with wrapping + highlights
cargo run --example todo_app # full workflow: state, input, routing, modalsAll examples (cargo run --example <name>)
hello_world counter async_state atom_state
router control_flow input_mutex input
search_input scrollview wrapped_text modal
confirm_modal alert_modal shortcut_info_modal select
multi_select tree_select table virtual_list
virtual_multi_select custom_widget custom_hook custom_provider
todo_app
Some examples require optional features such as input, tree, table, virtual-list, or router. Running examples from this repository uses the workspace configuration and enables full.
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.
- Find components — search the
ratatui-kitkeyword on crates.io, or browse awesome-ratatui-kit. - Write your own — scaffold from the
component template
(
cargo generate yexiyue/ratatui-kit-component-template) and follow the Component Guide. - Official extensions live in
ratatui-kit-contrib (e.g.
ratatui-kit-markdown— Markdown / code-block / diff / blockquote / divider).
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.
Issues and pull requests are welcome.
Before sending a PR, run the same validation matrix used by CI:
cargo fmt --all --check
cargo clippy --all-targets --all-features --workspace -- -D warnings
cargo test --locked --all-features --workspace --lib --tests --examples
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --document-private-items --all-features --workspace --examplesThis repository uses lefthook for local pre-commit checks.
Ratatui Kit is released under the MIT License.