Expand description
§RxTUI - Reactive Terminal User Interface Framework
A modern terminal UI framework for Rust that brings React-style component architecture and declarative UI building to the terminal. Build interactive, stateful terminal applications with ease using familiar patterns.
§Features
- Component-based architecture - Build reusable, composable UI components
- Declarative UI with
node!macro - Express your UI structure clearly - Virtual DOM with diffing - Efficient, minimal terminal updates
- Message-based state management - Predictable state updates like Elm
- Async effects - Handle background tasks, timers, and I/O operations
- Rich styling - Colors, borders, text styles, and layout control
- Built-in components - TextInput, forms, and more
§Architecture Overview
┌────────────┐ ┌─────────────┐ ┌─────────────┐
│ Component │────▶│ node! │────▶│ Node │
│ (trait) │ │ macro │ │ Tree │
└────────────┘ └─────────────┘ └─────────────┘
│ │ │
│ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Update │ │ View │────▶│ VDom │
│ (messages) │ │ (render) │ │ (state) │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Action │ │ Diff │
│ (state) │ │ Engine │
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Terminal │
│ Render │
└─────────────┘§Quick Start
use rxtui::prelude::*;
#[derive(Component)]
struct Counter;
impl Counter {
#[update]
fn update(&self, _ctx: &Context, msg: &str, mut count: i32) -> Action {
match msg {
"inc" => Action::update(count + 1),
"dec" => Action::update(count - 1),
_ => Action::exit(),
}
}
#[view]
fn view(&self, ctx: &Context, count: i32) -> Node {
node! {
div(
pad: 2,
align: center,
w_frac: 1.0,
gap: 1,
@key(up): ctx.handler("inc"),
@key(down): ctx.handler("dec"),
@key(esc): ctx.handler("exit")
) [
text(format!("Count: {count}"), color: white, bold),
text("use ↑/↓ to change, esc to exit", color: bright_black)
]
}
}
}
fn main() -> std::io::Result<()> {
App::new()?.run(Counter)
}§Key Concepts
- Component: Main trait for building UI components with state management
- node! macro: Declarative macro for building UI trees with JSX-like syntax
- Node: Virtual representation of UI elements (divs, text, components)
- Context: Provides access to message handlers and component communication
- Action: Return type from update methods (update state, exit, etc.)
- App: Main application runner that manages the event loop
- VDom: Virtual DOM that tracks UI state and calculates diffs
§Examples
§Basic Hello World
use rxtui::prelude::*;
#[derive(Component)]
struct HelloWorld;
impl HelloWorld {
#[view]
fn view(&self, ctx: &Context) -> Node {
node! {
div(bg: blue, pad: 2, @key_global(esc): ctx.handler(())) [
text("Hello, Terminal!", color: white, bold),
text("Press Esc to exit", color: white)
]
}
}
}
fn main() -> std::io::Result<()> {
App::new()?.run(HelloWorld)
}§Using the node! Macro
The node! macro provides a declarative way to build UI trees:
use rxtui::prelude::*;
fn build_ui(ctx: &Context) -> Node {
node! {
div(
bg: black, // Background color
border_color: white, // Border color
border_style: rounded, // Border style
pad: 2, // Padding
gap: 1, // Gap between children
dir: vertical // Layout direction
) [
text("Title", color: yellow, bold),
div(dir: horizontal, gap: 2) [
div(bg: blue, w: 20, h: 5) [
text("Left Panel", color: white)
],
div(bg: green, w: 20, h: 5) [
text("Right Panel", color: white)
]
],
text("Status: Ready", color: bright_black)
]
}
}§State Management
Components manage state through messages and updates:
use rxtui::prelude::*;
#[derive(Clone, Default)]
struct TodoState {
items: Vec<String>,
input: String,
}
#[derive(Component)]
struct TodoApp;
impl TodoApp {
#[update]
fn update(&self, _ctx: &Context, msg: &str, mut state: TodoState) -> Action {
match msg {
"add" => {
if !state.input.is_empty() {
state.items.push(state.input.clone());
state.input.clear();
}
Action::update(state)
}
"clear" => {
state.items.clear();
Action::update(state)
}
_ => Action::exit()
}
}
#[view]
fn view(&self, ctx: &Context, state: TodoState) -> Node {
node! {
div(pad: 2, gap: 1) [
text("Todo List", color: yellow, bold),
div(gap: 1) [
...(state.items.iter().map(|item| {
node! { text(format!("• {}", item), color: white) }
}).collect::<Vec<_>>())
],
text(format!("{} items", state.items.len()), color: bright_black)
]
}
}
}§Async Effects
Handle background tasks with effects (requires effects feature):
use rxtui::prelude::*;
#[derive(Component)]
struct Timer;
impl Timer {
#[update]
fn update(&self, _ctx: &Context, tick: bool, seconds: u32) -> Action {
if !tick {
return Action::exit();
}
Action::update(seconds + 1)
}
#[view]
fn view(&self, ctx: &Context, seconds: u32) -> Node {
node! {
div(pad: 2, align: center, @key(esc): ctx.handler(false)) [
text(format!("Timer: {}s", seconds), color: white, bold),
text("Press Esc to stop", color: bright_black)
]
}
}
#[cfg(feature = "effects")]
#[effect]
async fn tick(&self, ctx: &Context) {
loop {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
ctx.send(true);
}
}
}§Rich Text Formatting
Create styled text with multiple segments:
use rxtui::prelude::*;
fn status_line() -> Node {
node! {
richtext [
text("Status: ", color: white),
text("Connected", color: green, bold),
text(" | ", color: bright_black),
text("CPU: ", color: white),
text("42%", color: yellow),
text(" | ", color: bright_black),
text("Mem: ", color: white),
text("2.1GB", color: cyan)
]
}
}§Input Handling
Use the built-in TextInput component:
use rxtui::prelude::*;
fn input_form(ctx: &Context) -> Node {
node! {
div(pad: 2, gap: 1) [
text("Enter your name:", color: white),
input(
placeholder: "Type here...",
border_color: cyan,
w: 30,
focusable,
@submit: ctx.handler("submit")
)
]
}
}Re-exports§
pub use app::App;pub use app::Context;pub use bounds::Rect;pub use component::Action;pub use component::Component;pub use component::Message;pub use component::MessageExt;pub use component::State;pub use components::ShimmerSpeed;pub use components::ShimmerText;pub use components::TextInput;pub use key::Key;pub use key::KeyWithModifiers;pub use node::Div;pub use node::Node;pub use node::RichText;pub use node::Text;pub use node::TextSpan;pub use style::BorderEdges;pub use style::BorderStyle;pub use style::Color;pub use style::Dimension;pub use style::Direction;pub use style::Overflow;pub use style::Position;pub use style::Spacing;pub use style::Style;pub use style::TextStyle;pub use style::TextWrap;pub use style::WrapMode;
Modules§
- app
- Application framework for building terminal UIs. Provides the main application lifecycle and event handling.
- bounds
- Bounds and rectangle operations for dirty region tracking. Provides types for tracking screen regions that need redrawing. Bounds and rectangle operations for dirty region tracking.
- component
- New component-based system (parallel implementation)
- components
- Reusable UI components for building forms and interfaces Provides pre-built components like TextInput, Button, etc. Reusable UI components for rxtui
- effect
- Async effects system for running background tasks Async effects system for running background tasks in components
- key
- Key representation for keyboard input. Provides an enum for representing both characters and special keys. Key representation for keyboard input handling.
- macros
- Macro-based DSL for building TUI components Provides ergonomic macros for composing components with less boilerplate Macro-based DSL for building TUI components
- node
- Node types for component tree (includes div, text, rich_text)
- prelude
- Prelude module for convenient imports Prelude module for convenient imports.
- style
- Styling system for terminal UI components. Defines colors, spacing, borders, and other visual properties. Styling system for terminal UI models.
Macros§
- debug_
log - Debug logging macro that only compiles in debug builds. Writes timestamped messages to /tmp/radical_debug.log
- node
- Main macro for building TUI components with a declarative syntax
Attribute Macros§
- component
- Impl-level macro that automatically handles Component trait boilerplate.
- effect
- Marks an async method as a single effect that runs in the background.
- update
- Simplifies component update methods by automatically handling message downcasting, state fetching, and topic routing.
- view
- Simplifies component view methods by automatically fetching state from the context.