Skip to content

tui-dispatch

A centralized state management framework for Rust TUI applications, inspired by Redux and The Elm Architecture.

Quick Start

Get a running app in minutes with runtime helpers and sensible defaults.

Start building →

Async Patterns

Handle API calls, file I/O, and timers with effects and TaskManager.

Learn async →

Examples

From a simple counter to full apps with debug overlays and feature flags.

Browse examples →

When to Use tui-dispatch

Good fit:

  • Apps with shared state across multiple UI components
  • Apps with async operations (API calls, file I/O)
  • Apps where you want clear separation between UI and logic
  • Apps that need debugging tools (action logging, state inspection)

Overkill for:

  • Simple single-screen apps with minimal state
  • Apps where state is naturally local to each widget

How It Works

A reducer takes state and an action, returns whether state changed:

fn reducer(state: &mut AppState, action: AppAction) -> bool {
match action {
AppAction::Increment => { state.count += 1; true }
AppAction::Quit => false,
}
}

Use DispatchRuntime or Store directly. See the Counter example.

Adding Effects

When you need async operations, layer effects on top. The reducer returns DispatchResult instead of bool:

fn reducer(state: &mut AppState, action: AppAction) -> DispatchResult<Effect> {
match action {
AppAction::Fetch => {
state.is_loading = true;
DispatchResult::changed_with(Effect::FetchData)
}
AppAction::DidLoad(data) => {
state.data = Some(data);
state.is_loading = false;
DispatchResult::changed()
}
}
}

Effects are data describing side effects. The main loop executes them outside the reducer. Use EffectRuntime or EffectStore. See the Weather example.

Data Flow

Terminal Input
EventBus.handle_event(event) ──► Actions
store.dispatch(action)
reducer(state, action)
┌───────────┴───────────┐
│ │
▼ ▼
state changed? effects to run?
│ │
▼ ▼
render() handle_effect()
spawn async task
action_tx.send(DidAction)
└──► back to dispatch

Crate Structure

tui-dispatch/
├── tui-dispatch/ # Re-exports + prelude
├── tui-dispatch-core/ # Store, EffectStore, Runtime, Component, Keybindings, Middleware
├── tui-dispatch-macros/ # #[derive(Action)], #[derive(DebugState)], #[derive(BindingContext)]
└── tui-dispatch-debug/ # Debug overlay, action recording, replay, schema generation

Optional companion crate:

tui-dispatch-components/ # Reusable UI components: SelectList, TextInput, etc.

The core crate also includes Keybindings for context-aware key mapping, Middleware for action interception, and reducer_compose! for splitting large reducers.

Real-World Usage

tui-dispatch is used in production by memtui, a TUI for Redis, Memcached, and etcd.