revue 2.42.2

A Vue-style TUI framework for Rust with CSS styling
Documentation

/rɪˈvjuː/Re + Vue — A Vue-style TUI framework for Rust

crates.io docs.rs GitHub Stars

CI codecov license Rust 1.87+ downloads

Quick Start · Tutorials · Examples · Documentation · Contributing

Why Revue?

Build terminal UIs like you build web apps — with CSS and reactive state.

  • CSS Styling — Write styles in familiar CSS syntax with variables, selectors, and animations
  • Reactive State — Vue-inspired Signal/Computed/Effect system for automatic UI updates
  • 100+ Widgets — Rich component library: inputs, tables, charts, markdown, images, and more
  • Hot Reload — See CSS changes instantly without restarting your app
  • Developer Tools — Widget inspector, snapshot testing, and performance profiler built-in
  • Single Binary — Pure Rust, no runtime dependencies, blazing fast

Quick Start

cargo add revue
use revue::prelude::*;

fn main() -> Result<()> {
    let mut app = App::builder()
        .style("styles.css")
        .build();

    let counter = Counter::new();
    app.run(counter, |event, counter, _app| {
        if let Event::Key(key) = event {
            counter.handle_key(&key.key)
        } else {
            false
        }
    })
}

struct Counter {
    count: Signal<i32>,
}

impl Counter {
    fn new() -> Self {
        Self { count: signal(0) }
    }

    fn handle_key(&mut self, key: &Key) -> bool {
        match key {
            Key::Up => { self.count.update(|n| *n += 1); true }
            Key::Down => { self.count.update(|n| *n -= 1); true }
            _ => false,
        }
    }
}

impl View for Counter {
    fn render(&self, ctx: &mut RenderContext) {
        let count = self.count.get();
        vstack()
            .class("container")
            .child(Text::new(format!("Count: {}", count)).bold())
            .child(
                hstack().gap(1)
                    .child(Text::new("↑/↓ to change, q to quit"))
            )
            .render(ctx);
    }
}
/* styles.css */
.container {
    padding: 2;
    gap: 1;
    border: rounded cyan;
    align-items: center;
}

button {
    padding: 0 2;
    background: var(--primary);
}

button:hover {
    background: var(--primary-dark);
}

Key Features

Reactive Forms

Automatic validation with type-safe form state:

use revue::patterns::form::FormState;

let form = FormState::new()
    .field("email", |f| f
        .label("Email")
        .email()
        .required())
    .field("password", |f| f
        .label("Password")
        .password()
        .min_length(8))
    .field("confirm", |f| f
        .label("Confirm Password")
        .password()
        .matches("password"))
    .build();

// Reactive validation - errors auto-update when values change
form.set_value("email", "invalid");
assert!(!form.is_valid());

form.set_value("email", "user@example.com");
// Validation automatically recalculates

See Forms Tutorial for complete guide.

Animation System

Rich animations with easing functions and keyframes:

use revue::animation::{Animation, Easing};

// Fade in with custom easing
text("Hello!")
    .animation(Animation::fade_in()
        .duration(300)
        .easing(Easing::EaseInOutCubic))

// Slide in from left
text("Welcome!")
    .animation(Animation::slide_in_left()
        .duration(500)
        .delay(100))

// Keyframe animation
text("Pulsing!")
    .animation(Animation::keyframe(|keyframes| {
        keyframes
            .at(0, |kf| kf.scale(1.0).opacity(1.0))
            .at(50, |kf| kf.scale(1.2).opacity(0.8))
            .at(100, |kf| kf.scale(1.0).opacity(1.0))
    }))

Worker Pool

Execute background tasks without blocking the UI:

use revue::worker::{WorkerHandle, WorkerPool};

// Spawn blocking task
let handle = WorkerHandle::spawn_blocking(|| {
    heavy_computation()
});

// Use worker pool
let pool = WorkerPool::new(4);
pool.submit(|| {
    fetch_data_from_api()
});

// Get result when ready
if let Some(result) = handle.try_recv() {
    // Update UI with result
}

Hot Reload

CSS changes update instantly without restart:

let mut app = App::builder()
    .style("styles.css")
    .hot_reload(true)  // Enable hot reload
    .build();

app.run(view, handler)?;

Edit styles.css and see changes immediately - no restart needed!

DevTools

Built-in widget inspector and profiler:

let mut app = App::builder()
    .devtools(true)  // Enable devtools
    .build();

app.run(view, handler)?;

Keyboard shortcuts:

  • Ctrl+D — Toggle devtools overlay
  • Ctrl+I — Open widget inspector

Features:

  • Widget inspector with computed styles
  • Performance profiler for identifying bottlenecks
  • Snapshot testing for UI regression testing

Widgets

Category Components
Layout vstack hstack grid scroll tabs accordion splitter layers
Input input textarea select checkbox radio switch slider number_input
Forms form form_field — Built-in validation system
Display text markdown table tree list progress badge image presentation
Feedback modal toast notification tooltip popover alert callout
Charts barchart line_chart sparkline heatmap gauge boxplot histogram
Advanced rich_text_editor json_viewer csv_viewer diagram command_palette
Dev debug_overlay snapshot_test profiler

100+ Widgets — See FEATURES.md for complete catalog

Key Features

Reactive Forms

Automatic validation with type-safe form state:

use revue::patterns::form::FormState;

let form = FormState::new()
    .field("email", |f| f
        .label("Email")
        .email()
        .required())
    .field("password", |f| f
        .label("Password")
        .password()
        .min_length(8))
    .field("confirm", |f| f
        .label("Confirm Password")
        .password()
        .matches("password"))
    .build();

// Reactive validation - errors auto-update when values change
form.set_value("email", "invalid");
assert!(!form.is_valid());

form.set_value("email", "user@example.com");
// Validation automatically recalculates

See Forms Tutorial for complete guide.

Animation System

Rich animations with easing functions and keyframes:

use revue::animation::{Animation, Easing};

// Fade in with custom easing
text("Hello!")
    .animation(Animation::fade_in()
        .duration(300)
        .easing(Easing::EaseInOutCubic))

// Slide in from left
text("Welcome!")
    .animation(Animation::slide_in_left()
        .duration(500)
        .delay(100))

// Keyframe animation
text("Pulsing!")
    .animation(Animation::keyframe(|keyframes| {
        keyframes
            .at(0, |kf| kf.scale(1.0).opacity(1.0))
            .at(50, |kf| kf.scale(1.2).opacity(0.8))
            .at(100, |kf| kf.scale(1.0).opacity(1.0))
    }))

Worker Pool

Execute background tasks without blocking the UI:

use revue::worker::{WorkerHandle, WorkerPool};

// Spawn blocking task
let handle = WorkerHandle::spawn_blocking(|| {
    heavy_computation()
});

// Use worker pool
let pool = WorkerPool::new(4);
pool.submit(|| {
    fetch_data_from_api()
});

// Get result when ready
if let Some(result) = handle.try_recv() {
    // Update UI with result
}

Hot Reload

CSS changes update instantly without restart:

let mut app = App::builder()
    .style("styles.css")
    .hot_reload(true)  // Enable hot reload
    .build();

app.run(view, handler)?;

Edit styles.css and see changes immediately - no restart needed!

DevTools

Built-in widget inspector and profiler:

let mut app = App::builder()
    .devtools(true)  // Enable devtools
    .build();

app.run(view, handler)?;

Keyboard shortcuts:

  • Ctrl+D — Toggle devtools overlay
  • Ctrl+I — Open widget inspector

Features:

  • Widget inspector with computed styles
  • Performance profiler for identifying bottlenecks
  • Snapshot testing for UI regression testing

Examples

# Basics
cargo run --example counter       # Reactive counter with Signal
cargo run --example todo          # Full-featured todo app
cargo run --example hello_world   # Minimal "Hello World"

# UI Components
cargo run --example form          # Form validation demo
cargo run --example dashboard     # Charts and data widgets
cargo run --example gallery       # Widget showcase

# Advanced
cargo run --example animations    # Animation system
cargo run --example worker        # Background tasks
cargo run --example slideshow     # Terminal presentations
cargo run --example ide           # Rich text editor

# Real-world
cargo run --example chat          # Multi-user chat
cargo run --example data_explorer # JSON/CSV viewer

Browse all examples in the examples/ directory.

Tutorials

Tutorial Description Time
Getting Started Install and create your first app 5 min
Counter App Learn state management with signals 10 min
Todo App Build a full-featured todo list 20 min
Reactive State Deep dive into Signal, Computed, Effect 15 min
Styling CSS styling and theming 15 min
Forms Form handling with validation 20 min

Guides

Guide Description
App Builder Complete App Builder API reference
Styling CSS properties, selectors, and theming
State Management Reactive state with signals
Testing Test your TUI apps
Accessibility Build inclusive apps
Performance Optimization tips
Plugin System Create and use plugins

Comparison

Revue Ratatui Cursive Textual
Language Rust Rust Rust Python
Styling CSS Code Theme CSS
Reactivity Signal Manual Event Reactive
Forms ✅ Built-in
Animation ✅ Tween+Keyframes
Worker Pool
Hot Reload
Devtools
Single Binary

Documentation

Contributing

We welcome contributions! See CONTRIBUTING.md for guidelines.

git clone https://github.com/hawk90/revue.git
cd revue && cargo test

Development workflow:

  1. Fork the repository
  2. Create a feature branch (feat/your-feature, fix/your-bug)
  3. Make your changes with tests
  4. Run cargo test and cargo clippy
  5. Submit a pull request

License

MIT License — see LICENSE for details.

↑ Back to Top

crates.io · docs.rs · GitHub

Built with Rust