docs/enterprise/react_flow/interactivity.md
This guide shows how to create an interactive flow in Reflex, allowing you to select, drag, and connect nodes and edges.
We start by defining the nodes and edges of the flow. The FlowState class holds the nodes and edges as state variables and includes event handlers to respond to changes.
import reflex as rx
import reflex_enterprise as rxe
from reflex_enterprise.components.flow.types import Node, Edge
class FlowState(rx.State):
nodes: list[Node] = [
{
"id": "1",
"type": "input",
"position": {"x": 100, "y": 100},
"data": {"label": "Node 1"},
},
{
"id": "2",
"type": "default",
"position": {"x": 300, "y": 200},
"data": {"label": "Node 2"},
},
]
edges: list[Edge] = [
{
"id": "e1-2",
"source": "1",
"target": "2",
"label": "Connection",
"type": "step",
}
]
Event handlers allow the flow to respond to user interactions such as dragging nodes, updating edges, or creating new connections.
@rx.event
def set_nodes(self, nodes: list[Node]):
self.nodes = nodes
@rx.event
def set_edges(self, edges: list[Edge]):
self.edges = edges
set_nodes updates nodes when they are moved or edited.
set_edges updates edges when they are modified or deleted.
Note that these are plain setters — they receive the already-updated list. The change-application logic lives in the component wiring, shown next.
Finally, we render the flow using rxe.flow, passing in the state and event handlers. Additional UI features include zoom/pan controls, a background grid, and a mini-map for navigation.
def interactive_flow():
return rx.box(
rxe.flow(
rxe.flow.controls(),
rxe.flow.background(),
rxe.flow.mini_map(),
nodes=FlowState.nodes,
edges=FlowState.edges,
on_nodes_change=lambda node_changes: FlowState.set_nodes(
rxe.flow.util.apply_node_changes(FlowState.nodes, node_changes)
),
on_edges_change=lambda edge_changes: FlowState.set_edges(
rxe.flow.util.apply_edge_changes(FlowState.edges, edge_changes)
),
on_connect=lambda connection: FlowState.set_edges(
rxe.flow.util.add_edge(connection, FlowState.edges)
),
fit_view=True,
attribution_position="bottom-right",
),
height="100vh",
width="100vw",
)
on_nodes_change fires when nodes are dragged, selected, resized, or removed.on_edges_change fires when edges are selected or removed.on_connect fires when the user drags a connection between two handles; rxe.flow.util.add_edge builds the new edge list from the connection.Make sure to set the height and width of the container around the flow — the flow fills its parent, so without explicit dimensions it will not be visible.
Passing nodes and edges makes the flow controlled: your state is the source of truth, and the canvas renders exactly what the state contains. A controlled flow must wire on_nodes_change and on_edges_change as shown above, otherwise user interactions (dragging, selecting, deleting) are discarded.
Passing only default_nodes and default_edges makes the flow uncontrolled: the component manages changes internally, but your state never learns about them.
rxe.flow.util.apply_node_changes and rxe.flow.util.apply_edge_changes must be used inside of component code — in the lambda wired to on_nodes_change / on_edges_change — and never inside of state code. They evaluate on the client and produce the updated list, which is then passed to a plain setter event handler:
rxe.flow(
...,
on_nodes_change=lambda changes: FlowState.set_nodes(
rxe.flow.util.apply_node_changes(FlowState.nodes, changes)
),
)
Do not call these utilities from within an @rx.event handler; the state side should only receive and store the resulting list.