| Crates.io | rxtui |
| lib.rs | rxtui |
| version | 0.1.5 |
| created_at | 2025-08-22 07:42:38.102052+00 |
| updated_at | 2025-09-23 18:52:44.868793+00 |
| description | A framework for building beautiful, responsive terminal user interfaces with a DOM-style hierarchical approach |
| homepage | https://github.com/microsandbox/rxtui |
| repository | https://github.com/microsandbox/rxtui |
| max_upload_size | |
| id | 1806021 |
| size | 751,128 |
Terminal UIs have traditionally been painful to build. You either work with low-level escape sequences or use immediate-mode libraries that require manual state management. RxTUI brings the retained-mode, component-based architecture that revolutionized web development to the terminal.
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_pct: 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)
}
node! MacroBuild UIs declaratively with a JSX-like syntax:
node! {
div(bg: black, pad: 2, border_color: white) [
text("Hello, Terminal!", 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)
]
]
]
}
Components manage their own state and handle messages:
#[derive(Component)]
struct TodoList;
impl TodoList {
#[update]
fn update(&self, ctx: &Context, msg: TodoMsg, mut state: TodoState) -> Action {
match msg {
TodoMsg::Add(item) => {
state.items.push(item);
Action::update(state)
}
TodoMsg::Remove(idx) => {
state.items.remove(idx);
Action::update(state)
}
_ => Action::none()
}
}
#[view]
fn view(&self, ctx: &Context, state: TodoState) -> Node {
// Build your UI using the current state
}
}
Flexbox-inspired layout with:
Create styled text with multiple segments:
node! {
richtext [
text("Status: ", color: white),
text("Connected", color: green, bold),
text(" | ", color: bright_black),
text("CPU: ", color: white),
text("42%", color: yellow)
]
}
Handle background tasks with the effects system:
use std::time::Duration;
#[derive(Component)]
struct Timer;
impl Timer {
#[effect]
async fn tick(&self, ctx: &Context) {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
ctx.send("tick");
}
}
}
The examples directory contains comprehensive demonstrations:
Run examples with:
cargo run --example counter
effects (default) - Async effects support with tokioThis project is licensed under the Apache License 2.0 - see the LICENSE file for details.