rataflow
Interactive node-based UIs for the terminal.
What is it?
What it is
A Rust library for building interactive node-based UIs in the terminal, built on ratatui and inspired by xyflow.
Why it exists
It provides off-screen rendering, manual z-ordering, box-drawing edge merging, and a coordinate pipeline to overcome terminal limitations for node editors.
Who should use it
Rust developers building node-based UIs, flow/graph editors, or interactive diagrams for terminal TUI applications, especially those already using ratatui or migrating from web-based flow editors like xyflow/React Flow. Suitable for tools needing offline, lightweight graph editors, scripting pipelines with visual authoring, or WASM-deployed terminal-style interfaces.
Who should avoid it
Developers who need GUI/desktop-native node editors (this is terminal-only and cell-grid based), teams not using Rust (no bindings to other languages), users needing full browser-style WYSIWYG with images/rich media (terminal cells have severe constraints), and those wanting a drop-in replacement for React Flow without rewriting in Rust. Beginners to Rust or to TUI development will also struggle.
How it works
A quick walkthrough in plain English
How rataflow works
Step 1 of 3
You interact with it
Open rataflow, send a request, or connect it to your stack.
Features
Advantages
- Renders node-based UIs in the terminal using a single Flow widget with no separate state object to keep in sync
- Inspired by xyflow (React Flow), giving familiar API semantics for developers coming from web flow libraries
- Built on ratatui, leveraging the most popular Rust TUI framework
- Generic over content types, allowing fully custom node and edge rendering
- Production-quality rendering pipeline that handles negative coordinates, off-screen clipping, and box-drawing glyph merging
- Strong performance: ~1ms drag frame at 625 nodes and ~18ms at 40,000 nodes natively
- WASM support via ratzilla with every example runnable in the browser
- Competitive WASM performance vs xyflow, roughly 16x more nodes at equivalent frame time
- Multiple backend support: crossterm, termion, termwiz, and WASM
- MIT licensed and open source
- Extensive example coverage for every major feature (basic, multi-select, custom nodes/edges, custom layout, events, hierarchy, theming, save/restore, undo/redo)
- Optional serde support for persistence and undo/redo workflows
- Optional Sugiyama layout that can be compiled out when using custom layouts
- Comprehensive documentation including architecture and internals guides plus a blog series
Disadvantages
- Terminal cell grid is fundamentally lower fidelity than a browser canvas, limiting visual richness
- Terminal backends deliver raw mouse events at 125-1000Hz requiring manual event draining to avoid input lag
- WASM rendering carries roughly 8x overhead vs native due to WebGL2 and browser frame scheduling
- Project is at 0.1 version (pre-1.0), so APIs may still change
- Only 128 stars, indicating a relatively small user base and limited community contributions
- Documentation primarily focuses on Rust developers; teams using other languages cannot benefit
- Performance degrades significantly with grid topologies (~33ms for 37,500 nodes/74,600 edges)
- Requires understanding of Rust generics and ownership to customize node and edge content types effectively
Installation
native
cargo add rataflow Or add to your Cargo.toml: [dependencies] rataflow = "0.1"
FAQ
How do I create a basic flow with nodes and edges?
You can start with just a list of edges – nodes are created from the unique names in those edges, and positions come from the layout you provide. For example, `let mut flow = Flow::from_edges(&[("Start", "Process"), ("Process", "End")], Sugiyama::vertical())?;`. This gives you a draggable, pannable, and zoomable graph from the first frame.
How can I react to user interactions such as node clicks or completed connections?
Event handlers receive `FlowEvent`s returned by `flow.handle_mouse_event` or `flow.handle_key_event`. Each event is one of the semantic variants (e.g., `NodeClicked`, `ConnectionCompleted`, `SelectionChanged`). You iterate over the events and update your UI or graph state accordingly – for instance, adding an edge after a connection is completed: `flow.add_edge_from_connection(conn, StepEdge::default());`.
What are the companion widgets and how do I use them?
The library provides three companion widgets that wrap a `Flow`: `Background`, `Controls`, and `MiniMap`. Each borrows a reference to the same `Flow` and can be rendered alongside it in your terminal draw loop. They add common UI elements like a zoomable background grid, zoom/pan controls, and a minimap of the whole graph without extra logic on your part.
How does rataflow support custom node and edge rendering?
Nodes and edges are generic over `NodeContent` and `EdgeContent`. You define your own types, implement rendering logic (or use the built‑in `TextContent` and `StepEdge`), and pass them when constructing the graph. The library then renders them exactly as you specify, giving you full control over appearance while still providing default types for quick prototyping.
How can I persist changes or support undo/redo?
The crate includes serde serialization for graph snapshots, which you can use to save and restore the entire graph state. For undo/redo, you can keep a history of snapshots (or use the provided `UndoRedo` helper) and apply them back into the `Flow` via its mutation API. This keeps layout, hierarchy, and indices consistent across saves.
What backends and feature flags are supported?
rataflow works with several terminal backends: `crossterm` (default), `termion`, `termwiz`, and `ratzilla` for WebAssembly. Feature flags control optional parts like the Sugiyama layout (`sugiyama`, default), serialization (`serde`), and each backend’s event conversion. Enable only what you need to keep your binary small.
Featured in Videos
YouTube tutorials and walkthroughs for rataflow
Alternatives
Similar projects ranked by category, topics, and text overlap.