| Crates.io | reratui |
| lib.rs | reratui |
| version | 1.1.0 |
| created_at | 2025-11-01 20:23:17.022453+00 |
| updated_at | 2026-01-16 09:05:37.214703+00 |
| description | A modern, reactive TUI framework for Rust with React-inspired hooks and components, powered by ratatui |
| homepage | https://github.com/sabry-awad97/reratui |
| repository | https://github.com/sabry-awad97/reratui |
| max_upload_size | |
| id | 1912390 |
| size | 997,087 |
A modern, reactive TUI framework for Rust with React-inspired hooks and components
Features • Quick Start • Examples • Documentation • Contributing
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.
🎯 React-like Component Model
Component trait to create reusable components🔄 Fiber Architecture
🪝 Comprehensive Hooks System
use_state - Local state with batched updatesuse_effect - Side effects with cleanupuse_context - Share data across component treeuse_memo / use_callback - Memoizationuse_reducer - Complex state managementuse_ref - Mutable references without re-renders⚡ Async First
use_future - Track async task stateuse_query - Data fetching with caching & retryuse_mutation - Mutation state trackinguse_async_effect - Async side effects🎮 Rich Event Handling
use_keyboard / use_keyboard_shortcutuse_mouse / use_mouse_hover / use_mouse_draguse_resize / use_media_query⏱️ Timing & Utilities
use_timeout / use_intervaluse_history - Undo/redo supportuse_form - Form validationuse_id - Unique IDs📜 Scrolling
ScrollView - Generic scrollable container for any widgetScrollViewItems - Optimized scrolling for item listsuse_scroll / use_scroll_keyboard - Scroll state hooksAdd 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(())
}
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 |
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);
}
}
_ => {}
}
}
}
// 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(),
_ => {}
}
});
// 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...
}
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
}
Reratui uses a 5-phase render pipeline:
┌─────────────────────────────────────────────────────────┐
│ Render Loop │
│ │
│ ┌──────┐ ┌────────┐ ┌────────┐ ┌──────────┐ │
│ │ Poll │ → │ Render │ → │ Commit │ → │ Event │ │
│ └──────┘ └────────┘ └────────┘ └──────────┘ │
│ ↑ │ │
│ │ ┌────────┐ │ │
│ └─────────│ Effect │←────────────────────┘ │
│ └────────┘ │
└─────────────────────────────────────────────────────────┘
| 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 |
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);
use_effect(|| { /* effect */ Some(Box::new(|| { /* cleanup */ })) }, deps);
use_effect_once(|| { /* runs once on mount */ None });
use_async_effect(|| async { /* async effect */ None }, deps);
use_context_provider(|| value);
let value = use_context::<T>();
let maybe_value = try_use_context::<T>();
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);
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);
let timeout = use_timeout(|| { /* callback */ }, delay_ms);
let interval = use_interval(|| { /* callback */ }, interval_ms);
let area = use_area();
let frame = use_frame();
let (width, height) = use_resize();
let is_narrow = use_media_query(|(w, _)| w < 80);
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
└── ...
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
# Run tests
cargo test
# Run a specific example
cargo run -p counter-fiber
# Check formatting
cargo fmt --check
# Run clippy
cargo clippy
Licensed under either of:
at your option.
Made with ❤️ for the Rust TUI community