reratui

Crates.ioreratui
lib.rsreratui
version1.1.0
created_at2025-11-01 20:23:17.022453+00
updated_at2026-01-16 09:05:37.214703+00
descriptionA modern, reactive TUI framework for Rust with React-inspired hooks and components, powered by ratatui
homepagehttps://github.com/sabry-awad97/reratui
repositoryhttps://github.com/sabry-awad97/reratui
max_upload_size
id1912390
size997,087
Sabry Awad (sabry-awad97)

documentation

https://docs.rs/reratui

README

Reratui Logo

Reratui

A modern, reactive TUI framework for Rust with React-inspired hooks and components

Crates.io Downloads Documentation CI Status License Rust Version

FeaturesQuick StartExamplesDocumentationContributing


Reratui brings React's powerful component model and hooks system to terminal user interfaces in Rust. Built on top of ratatui, it provides a familiar, declarative approach to building complex TUI applications with proper state management, effects, and async support.

Features

🎯 React-like Component Model

  • Implement the Component trait to create reusable components
  • Compose components naturally with Rust's type system
  • Props as struct fields with full type safety

🔄 Fiber Architecture

  • Efficient reconciliation and rendering pipeline
  • Batched state updates for optimal performance
  • Dirty tracking to minimize re-renders

🪝 Comprehensive Hooks System

  • use_state - Local state with batched updates
  • use_effect - Side effects with cleanup
  • use_context - Share data across component tree
  • use_memo / use_callback - Memoization
  • use_reducer - Complex state management
  • use_ref - Mutable references without re-renders

Async First

  • use_future - Track async task state
  • use_query - Data fetching with caching & retry
  • use_mutation - Mutation state tracking
  • use_async_effect - Async side effects

🎮 Rich Event Handling

  • use_keyboard / use_keyboard_shortcut
  • use_mouse / use_mouse_hover / use_mouse_drag
  • use_resize / use_media_query

⏱️ Timing & Utilities

  • use_timeout / use_interval
  • use_history - Undo/redo support
  • use_form - Form validation
  • use_id - Unique IDs

📜 Scrolling

  • ScrollView - Generic scrollable container for any widget
  • ScrollViewItems - Optimized scrolling for item lists
  • use_scroll / use_scroll_keyboard - Scroll state hooks

Quick Start

Add Reratui to your Cargo.toml:

[dependencies]
reratui = "1.1"
tokio = { version = "1.49", features = ["full"] }

Create your first component:

use reratui::prelude::*;

struct Counter;

impl Component for Counter {
    fn render(&self, area: Rect, buffer: &mut Buffer) {
        // State persists across renders
        let (count, set_count) = use_state(|| 0);

        // Handle keyboard input
        use_keyboard_press(move |key| {
            match key.code {
                KeyCode::Up => set_count.update(|c| c + 1),
                KeyCode::Down => set_count.update(|c| c.saturating_sub(1)),
                KeyCode::Char('q') => request_exit(),
                _ => {}
            }
        });

        // Render UI using ratatui widgets
        let block = Block::default()
            .title("Counter")
            .borders(Borders::ALL)
            .border_style(Style::default().fg(Color::Cyan));

        Paragraph::new(format!("Count: {}", count))
            .block(block)
            .alignment(Alignment::Center)
            .render(area, buffer);
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    render(|| Counter).await?;
    Ok(())
}

Examples

The repository includes several examples demonstrating various features:

Example Description Run Command
counter_fiber Basic state management cargo run -p counter-fiber
counter Simple counter (legacy) cargo run -p counter
effect_timing Effect lifecycle cargo run -p effect_timing
async_fetch_example Async data fetching cargo run --example async_fetch_example
query_example Data queries with caching cargo run --example query_example
mutation_example CRUD operations cargo run --example mutation_example
events_showcase Keyboard & mouse events cargo run --example events_showcase
command_palette Complex multi-component app cargo run --example command_palette
data_fetcher Multiple async sources cargo run --example data_fetcher
ink_like_counter Ink-style component syntax cargo run --example ink_like_counter
scroll_example Scrollable content cargo run --example scroll_example

Data Fetching Example

use reratui::prelude::*;
use reratui::hooks::{use_query, QueryOptions, QueryStatus};

struct UserList;

impl Component for UserList {
    fn render(&self, area: Rect, buffer: &mut Buffer) {
        let query = use_query(
            "users",
            || async { fetch_users().await },
            Some(QueryOptions {
                stale_time: Duration::from_secs(30),
                retry: true,
                retry_attempts: 3,
                ..Default::default()
            }),
        );

        match query.status {
            QueryStatus::Loading => {
                Paragraph::new("Loading...").render(area, buffer);
            }
            QueryStatus::Success => {
                if let Some(users) = &query.data {
                    render_user_list(users, area, buffer);
                }
            }
            QueryStatus::Error => {
                if let Some(err) = &query.error {
                    Paragraph::new(format!("Error: {}", err))
                        .style(Style::default().fg(Color::Red))
                        .render(area, buffer);
                }
            }
            _ => {}
        }
    }
}

Keyboard Shortcuts

// Handle specific key combinations
use_keyboard_shortcut(
    KeyCode::Char('s'),
    KeyModifiers::CONTROL,
    || save_document(),
);

// Handle all key presses
use_keyboard_press(move |key| {
    match key.code {
        KeyCode::Up => navigate_up(),
        KeyCode::Down => navigate_down(),
        KeyCode::Enter => select_item(),
        _ => {}
    }
});

Mouse Interaction

// Track hover state
let button_area = Rect::new(10, 5, 20, 3);
let is_hovering = use_mouse_hover(button_area);

// Handle clicks
use_mouse_click(move |button, x, y| {
    if button == MouseButton::Left && button_area.contains((x, y).into()) {
        handle_button_click();
    }
});

// Track drag operations
let (drag_info, reset_drag) = use_mouse_drag();
if drag_info.is_dragging {
    // Handle drag...
}

Scrollable Content

use reratui::components::{ScrollView, ScrollViewProps};

// Generic widget scrolling - works with ANY widget
let props = ScrollViewProps::new(100, |content_area, buf| {
    // Render your content here - it will be scrollable!
    for i in 0..10 {
        let section_area = Rect::new(
            content_area.x,
            content_area.y + i * 10,
            content_area.width,
            10,
        );
        Block::default()
            .borders(Borders::ALL)
            .title(format!("Section {}", i + 1))
            .render(section_area, buf);
    }
})
.block(Block::default().borders(Borders::ALL).title("Scrollable"));

ScrollView::new(props).render(area, buffer);
use reratui::hooks::use_scroll_keyboard;

// Simple scroll hook with automatic keyboard bindings (j/k, arrows, etc.)
let scroll = use_scroll_keyboard(items.len(), viewport_height);

// Render only visible items
for (i, item) in items.iter().skip(scroll.offset).take(viewport_height).enumerate() {
    // render item at position i
}

Architecture

Reratui uses a 5-phase render pipeline:

┌─────────────────────────────────────────────────────────┐
│                     Render Loop                         │
│                                                         │
│   ┌──────┐    ┌────────┐    ┌────────┐    ┌──────────┐  │
│   │ Poll │ →  │ Render │ →  │ Commit │ →  │  Event   │  │
│   └──────┘    └────────┘    └────────┘    └──────────┘  │
│       ↑                                        │        │
│       │         ┌────────┐                     │        │
│       └─────────│ Effect │←────────────────────┘        │
│                 └────────┘                              │
└─────────────────────────────────────────────────────────┘
  1. Poll - Wait for terminal events or scheduled updates
  2. Render - Execute component render functions
  3. Commit - Apply batched state updates
  4. Event - Process terminal events
  5. Effect - Run effects and cleanup functions

Documentation

Document Description
Hooks Reference Complete API reference for all hooks
Component Guide How to create and compose components
Async Patterns Data fetching, queries, and mutations
Architecture Internal fiber system and render pipeline
Examples Guide Walkthrough of all examples
Migration Guide Upgrading from older versions
React Differences React vs Reratui comparison
Strict Mode Development mode for catching issues

Hooks at a Glance

State Management

let (value, set_value) = use_state(|| initial);
let (state, dispatch) = use_reducer(reducer, initial);
let ref_handle = use_ref(|| initial);
let history = use_history(|| initial);

Effects

use_effect(|| { /* effect */ Some(Box::new(|| { /* cleanup */ })) }, deps);
use_effect_once(|| { /* runs once on mount */ None });
use_async_effect(|| async { /* async effect */ None }, deps);

Context

use_context_provider(|| value);
let value = use_context::<T>();
let maybe_value = try_use_context::<T>();

Async Data

let future = use_future(|| async { Ok(data) }, Some(deps));
let query = use_query("key", || async { fetch() }, options);
let mutation = use_mutation(|args| async { mutate(args) }, options);

Events

if let Some(event) = use_event() { /* handle */ }
use_keyboard_press(|key| { /* handle */ });
use_keyboard_shortcut(KeyCode::Char('s'), KeyModifiers::CONTROL, || {});
use_mouse_click(|button, x, y| { /* handle */ });
let is_hovering = use_mouse_hover(area);

Timing

let timeout = use_timeout(|| { /* callback */ }, delay_ms);
let interval = use_interval(|| { /* callback */ }, interval_ms);

Layout

let area = use_area();
let frame = use_frame();
let (width, height) = use_resize();
let is_narrow = use_media_query(|(w, _)| w < 80);

Requirements

  • Rust 1.85.0 or later (edition 2024)
  • Tokio runtime for async support

Project Structure

reratui/
├── crates/
│   ├── reratui/           # Main library
│   │   ├── src/
│   │   │   ├── hooks/     # All hook implementations
│   │   │   ├── scheduler/ # State batching & effects
│   │   │   ├── fiber.rs   # Fiber node
│   │   │   ├── fiber_tree.rs
│   │   │   ├── runtime.rs # Main render loop
│   │   │   └── ...
│   │   └── docs/          # Detailed documentation
│   └── reratui-macro/     # Procedural macros
├── examples/              # Example applications
└── ...

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Development

# Run tests
cargo test

# Run a specific example
cargo run -p counter-fiber

# Check formatting
cargo fmt --check

# Run clippy
cargo clippy

License

Licensed under either of:

at your option.

Acknowledgments

  • ratatui - The excellent TUI library this is built upon
  • React - Inspiration for the component model and hooks system
  • Ink - Inspiration for bringing React patterns to terminals

Made with ❤️ for the Rust TUI community

Commit count: 0

cargo fmt