egui_xyflow
A node graph editor for egui. Build interactive flow charts, diagrams, pipelines, and visualizations — inspired by xyflow (React Flow).
Features
- Drag-and-drop nodes with box-select and multi-select
- Connect nodes by dragging between handles, with optional validation rules
- 5 edge types — Bezier, SmoothStep, Step, Straight, SimpleBezier
- Styled edges — per-edge colors, stroke widths, glow effects, dash animations, and text labels
- Pan & zoom — scroll, pinch, double-click, or keyboard shortcuts
- Minimap for navigating large graphs
- Resizable nodes — drag handles to resize
- Draggable edge anchors — reposition where edges attach to nodes
- Snap to grid for aligned layouts
- Background patterns — dots, lines, or cross
- Animated viewport transitions with easing functions
- Viewport edge culling — off-screen edges skip path compute (toggle via
FlowConfig::cull_offscreen_edges) - Force-directed layout — built-in
egui_xyflow::physicsmodule with Barnes–Hut charge, link, position, collision, and center forces (D3-compatible defaults) - Serde support — save and load graph state (enabled by default)
- Fully customizable — 60+ options in
FlowConfig, plus traits for custom node/edge rendering
Getting Started
Add to your Cargo.toml:
[]
= "0.4"
= "0.31"
Minimal Example
This creates two connected nodes:
use egui;
use *;
How It Works
Each frame follows a simple cycle:
FlowState → FlowCanvas::show() → FlowEvents → apply changes → FlowState
FlowState<ND, ED>holds your graph — nodes, edges, viewport, and configFlowCanvas::show(ui)renders the graph and returns what happened this frameFlowEventstells you about clicks, drags, new connections, selection changes, etc.- You react by applying
NodeChange/EdgeChangeback to the state
The two type parameters ND and ED are your custom data types attached to nodes and edges. Use () if you don't need them, or String for labels, or any type you want.
Building Nodes
Use the builder pattern to create nodes:
builder
.position // where to place it
.data // your custom data
.handle // output handle on the right
.handle // input handle on the left
.size // explicit size (optional)
.z_index // render order (optional)
.build
Handles are the connection points on a node. A source handle lets edges leave, a target handle lets edges arrive. Place them on any side: Top, Right, Bottom, Left, or use Closest to auto-track the nearest side.
Building Edges
// Simple edge (Edge::new and Edge::builder are interchangeable)
builder
// Styled edge with a text label
builder
.edge_type
.color
.stroke_width
.glow
.animated
.label
.marker_end_arrow
Label font/color/background/padding are configured on FlowConfig
(edge_label_font_size, edge_label_color, edge_label_bg_color,
edge_label_padding). Set edge_label_bg_color to
Color32::TRANSPARENT to disable the label background.
Edge types:
| Type | Description |
|---|---|
Bezier |
Smooth cubic curve (default) |
SimpleBezier |
Simplified bezier |
SmoothStep |
Orthogonal path with rounded corners |
Step |
Orthogonal path with sharp 90° corners |
Straight |
Direct line |
Reacting to Events
FlowCanvas::show() returns FlowEvents — inspect it to respond to user actions:
let events = new.show;
// New connections made by dragging between handles
for conn in &events.connections_made
// Nodes clicked
for id in &events.nodes_clicked
// Selection changed
if events.selection_changed
// Nodes deleted (via Delete/Backspace key)
for id in &events.nodes_deleted
// Viewport panned or zoomed
if events.viewport_changed
All available events:
| Event | Type | When it fires |
|---|---|---|
connections_made |
Vec<Connection> |
User completed a handle-to-handle drag |
connection_started |
Option<NodeId> |
User started dragging from a handle |
connection_ended |
bool |
Connection drag finished |
nodes_clicked |
Vec<NodeId> |
Short click on a node |
edges_clicked |
Vec<EdgeId> |
Click on an edge |
nodes_drag_started |
Vec<NodeId> |
Node drag began |
nodes_dragged |
Vec<(NodeId, Pos2)> |
Node moved to new position |
nodes_drag_stopped |
Vec<NodeId> |
Node drag ended |
nodes_resized |
Vec<(NodeId, f32, f32)> |
Node resized (id, width, height) |
nodes_deleted |
Vec<NodeId> |
Nodes removed via keyboard |
edges_deleted |
Vec<EdgeId> |
Edges removed via keyboard |
selection_changed |
bool |
Selection state changed |
selected_nodes |
Vec<NodeId> |
Currently selected nodes |
selected_edges |
Vec<EdgeId> |
Currently selected edges |
node_hovered |
Option<NodeId> |
Mouse is over a node |
edge_anchors_changed |
Vec<(EdgeId, ...)> |
Edge endpoints were dragged |
viewport_changed |
bool |
Pan, zoom, or animation occurred |
Applying Changes
Modify graph state through the change system:
use *;
// Move a node
state.apply_node_changes;
// Remove an edge
state.apply_edge_changes;
// Add a new node
state.apply_node_changes;
Viewport Control
Navigate the graph programmatically:
let time = ui.input;
let rect = ui.available_rect_before_wrap;
state.fit_view; // fit all nodes with padding
state.fit_selected_nodes; // fit selected nodes
state.zoom_in; // animated zoom in
state.zoom_out; // animated zoom out
state.set_center; // center on point at zoom 1.5
Validation
Control which connections are allowed:
;
// Use it when rendering
new
.connection_validator
.show;
Custom Node Rendering
Implement the NodeWidget trait to control how nodes look:
;
Built-in renderers:
DefaultNodeWidget— rendersNode<String>with label textUnitNodeWidget— rendersNode<()>as plain boxes
Custom Edge Rendering
Implement EdgeWidget for custom edge paths:
;
new
.edge_widget
.show;
Physics (Force-Directed Layout)
The egui_xyflow::physics module provides a D3-compatible force simulation
for laying out graphs automatically. Charge uses a Barnes–Hut quadtree
(θ = 0.9 by default) so it stays cheap for large graphs.
use *;
// Build a simulation from your FlowState.
let mut sim = from_state
.add_force
.add_force
.add_force
.add_force;
// Each frame — reads drag state, ticks forces, writes positions back.
// Returns `false` if `state` was mutated since construction; rebuild on false.
if !sim.step
Built-in forces:
| Force | Purpose | D3 analogue |
|---|---|---|
ManyBodyForce |
Pairwise repulsion/attraction (Barnes–Hut) | forceManyBody |
LinkForce |
Spring between connected node pairs | forceLink |
PositionForce |
Pull toward a target point | forceX / forceY |
CollisionForce |
Prevent overlap based on SimNode.radius |
forceCollide |
CenterForce |
Rigidly recenter the graph centroid | forceCenter |
Or implement the Force trait for custom behaviour. Per-node overrides
(radius, strength) can be set on SimNode directly, or derived from
your node data via ForceSimulation::from_state_with(&state, opts).
The module is not in the crate prelude — import it explicitly:
use egui_xyflow::physics::*;.
See examples/disjoint_force_graph.rs for a full citation-network
layout and examples/physics_bench.rs for a timing harness.
Configuration
FlowConfig has 60+ options. Here are the most commonly adjusted:
let mut config = default;
// Interaction
config.nodes_draggable = true;
config.nodes_connectable = true;
config.nodes_selectable = true;
config.nodes_resizable = true;
config.snap_to_grid = true;
config.snap_grid = ;
// Viewport
config.min_zoom = 0.1;
config.max_zoom = 4.0;
config.pan_on_drag = true;
config.zoom_on_scroll = true;
config.zoom_on_pinch = true;
// Appearance
config.default_edge_type = SmoothStep;
config.background_variant = Dots;
config.show_minimap = true;
// Colors
config.node_bg_color = from_rgb;
config.edge_color = from_rgb;
let state = new;
See the docs for the full list of options.
Examples
Clone the repo and run any of the 17 included examples:
Compatibility
| egui_xyflow | egui |
|---|---|
| 0.4 | 0.31 |
| 0.3 | 0.31 |
| 0.2 | 0.31 |
| 0.1 | 0.31 |
License
MIT