How I Run Agents Off a React Flow Canvas, Not a Diagram

7 min readYaseen Khatib · AI Architect

On IntegrateX I stopped treating the React Flow canvas as a place to sketch workflows and started running the agents directly off it — as the control plane for a running agent system. The same graph a PM drags to say "when a ticket comes in, search the docs, then either answer or escalate" is the exact spec the runtime executes — no shadow YAML, no parallel DSL. The moment you wire it this way, the diagram stops being decoration and becomes the program.

Nodes are capabilities, edges are contracts

Treat the canvas like a system, not clip art. A node is a capability: trigger, agent, tool, output. An edge is a typed contract that says one capability's output is valid input to the next. React Flow already gives you the primitives you need: custom node components and typed handles (ports). You're not fighting the library; you're finally using the part most demos skip.

CONTROL PLANE · active path litTriggerAgentreason · plan · calltooldonesearch_docs()create_ticket()response
Nodes are agents and tools; typed ports are the contract; the lit edge is the run actually executing.

Typed ports are the whole trick

The gap between a pretty drawing and a real orchestrator is whether connections carry meaning. Put a type on each handle — document stream, tool result, terminal "done" — and reject bad edges while the user is drawing. Entire categories of runtime bugs disappear, and your on-call future self stops diffing logs to learn someone piped a string into a tool port. If the port doesn't accept the edge, it can't ship broken.

ports.ts
// a handle carries a type; the canvas refuses incompatible edges
type PortType = "trigger" | "tool" | "text" | "done";

function isValidConnection(c: Connection, nodes: AgentNode[]) {
  const from = portType(nodes, c.source, c.sourceHandle);
  const to   = portType(nodes, c.target, c.targetHandle);
  return COMPATIBLE[from]?.includes(to) ?? false;
}

// <ReactFlow isValidConnection={isValidConnection} />

Separate the render graph from the run graph

Keep one invariant or you'll drown in state-synchronization lag: React Flow state is the render model — positions, selection, pan/zoom, visual edges. The executor consumes a derived run graph — capabilities and typed wiring, no UI fluff. I package this as the pattern I call Trinity Architecture: (1) Presentation — the canvas renders and dispatches events; (2) Reactive State / Orchestration — a client store owns the source of truth and optimistic updates; (3) Data / Serialization Adapter — a boundary that compiles rich in-memory state into lean wire payloads. On IntegrateX, that Serialization Adapter stripped React Flow metadata before persistence and cut payload size 94%, which killed a whole class of sync stalls and payload bloat. Boundary rule: the UI never formats DB schemas; the adapter never pokes UI state directly — only through the orchestrator.

Why managers care

A visual, typed graph is readable to a PM, tweakable by support, and debuggable by engineers watching the active path light up in real time. You stop spelunking imperative glue and start pointing at a living artifact. Keep the overlay cheap and state isolated, and you avoid render thrash while the system streams results; hard-won habit from streamerOS, where 60fps mattered. The payoff here is clarity the whole team can ship against.

The best agent architecture is the one a non-author can read. A typed node graph turns "trust me, the orchestration works" into something you can point at.

Next: compiling that canvas into a runnable pipeline, and the related pattern of routing between specialised agents . Shipping IntegrateX solo taught me that the architecture a whole team can read and change is worth more than the clever one only its author can — the judgment I bring is choosing the version that survives contact with the rest of the team.

Need an engineer who can build this?

I'm Yaseen Khatib — a Senior Full-Stack AI Engineer (MERN + TypeScript) who ships production AI systems solo. Open to senior and lead roles, remote or on-site.