rxtui

Crates.iorxtui
lib.rsrxtui
version0.1.5
created_at2025-08-22 07:42:38.102052+00
updated_at2025-09-23 18:52:44.868793+00
descriptionA framework for building beautiful, responsive terminal user interfaces with a DOM-style hierarchical approach
homepagehttps://github.com/microsandbox/rxtui
repositoryhttps://github.com/microsandbox/rxtui
max_upload_size
id1806021
size751,128
Stephen Akinyemi (appcypher)

documentation

https://docs.rs/rxtui

README

rxtui-dark rxtui-light

———   reactive terminal UI framework for rust   ———


crates.io version docs.rs license

Why RxTUI?

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.

  • Declarative UI - Describe what your UI should look like, not how to change it
  • Component Architecture - Build complex apps from simple, reusable components
  • Message-Based State - Elm-inspired architecture for predictable state updates
  • Efficient Rendering - Virtual DOM with intelligent diffing minimizes redraws
  • Rich Styling - Colors, borders, flexbox-style layout, text wrapping, and more
  • Built-in Components - TextInput, forms, and other common UI elements
  • Async Effects - First-class support for timers, API calls, and background tasks

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_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)
}

Features

The node! Macro

Build 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)
            ]
        ]
    ]
}

Component System

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
    }
}

Layout System

Flexbox-inspired layout with:

  • Direction control (horizontal/vertical)
  • Justify content (start, center, end, space-between, etc.)
  • Align items (start, center, end)
  • Wrapping support
  • Percentage and fixed sizing
  • Auto-sizing based on content

Rich Text Support

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)
    ]
}

Async Effects

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");
        }
    }
}

Examples

The examples directory contains comprehensive demonstrations:

  • counter - Minimal interactive counter
  • form - Text input and form handling
  • stopwatch - Timer with effects
  • align - Flexbox-style alignment
  • components - Component composition
  • demo - Full feature showcase

Run examples with:

cargo run --example counter

Documentation

Requirements

  • Rust 1.70 or later
  • Terminal with UTF-8 support
  • Unix-like system (Linux, macOS) or Windows 10+

Optional Features

  • effects (default) - Async effects support with tokio

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Commit count: 32

cargo fmt