Skip to content
Learn Agentic AI
Learn Agentic AI13 min read0 views

Building a Drag-and-Drop Agent Builder: Visual Workflow Editor with React

Create a visual agent workflow editor using React, drag-and-drop libraries, and a node-based canvas. Learn node rendering, connection drawing, and workflow serialization.

Why Visual Agent Builders Matter

Not every agent designer is a developer. Product managers, domain experts, and operations teams need to define agent workflows — which tools to call, when to escalate, how to route conversations — without writing code. A visual workflow editor lets them drag agent nodes onto a canvas, connect them with edges, and configure behavior through form panels. React Flow is the dominant library for building these interfaces in React.

Setting Up React Flow

React Flow provides the canvas, node rendering, edge drawing, and interaction handling. Install it and create a basic workflow editor.

flowchart TD
    START["Building a Drag-and-Drop Agent Builder: Visual Wo…"] --> A
    A["Why Visual Agent Builders Matter"]
    A --> B
    B["Setting Up React Flow"]
    B --> C
    C["Custom Agent Nodes"]
    C --> D
    D["The Node Palette with Drag-and-Drop"]
    D --> E
    E["Drop Handler on the Canvas"]
    E --> F
    F["Serializing the Workflow"]
    F --> G
    G["FAQ"]
    G --> DONE["Key Takeaways"]
    style START fill:#4f46e5,stroke:#4338ca,color:#fff
    style DONE fill:#059669,stroke:#047857,color:#fff
import {
  ReactFlow,
  Background,
  Controls,
  MiniMap,
  Node,
  Edge,
  useNodesState,
  useEdgesState,
  addEdge,
  Connection,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";

const initialNodes: Node[] = [
  {
    id: "triage",
    type: "agentNode",
    position: { x: 250, y: 50 },
    data: { label: "Triage Agent", model: "gpt-4o" },
  },
  {
    id: "support",
    type: "agentNode",
    position: { x: 100, y: 250 },
    data: { label: "Support Agent", model: "gpt-4o-mini" },
  },
  {
    id: "billing",
    type: "agentNode",
    position: { x: 400, y: 250 },
    data: { label: "Billing Agent", model: "gpt-4o-mini" },
  },
];

const initialEdges: Edge[] = [
  { id: "e1", source: "triage", target: "support", label: "support" },
  { id: "e2", source: "triage", target: "billing", label: "billing" },
];

function WorkflowEditor() {
  const [nodes, setNodes, onNodesChange] =
    useNodesState(initialNodes);
  const [edges, setEdges, onEdgesChange] =
    useEdgesState(initialEdges);

  const onConnect = (connection: Connection) => {
    setEdges((eds) => addEdge(connection, eds));
  };

  return (
    <div className="w-full h-[700px] border rounded-xl">
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        nodeTypes={nodeTypes}
        fitView
      >
        <Background />
        <Controls />
        <MiniMap />
      </ReactFlow>
    </div>
  );
}

Custom Agent Nodes

Default nodes are plain rectangles. Create custom nodes that display agent information with connection handles for inputs and outputs.

import { Handle, Position, NodeProps } from "@xyflow/react";

interface AgentNodeData {
  label: string;
  model: string;
}

function AgentNode({ data }: NodeProps) {
  const nodeData = data as unknown as AgentNodeData;

  return (
    <div className="bg-white border-2 border-blue-200 rounded-xl
                    shadow-md px-4 py-3 min-w-[180px]">
      <Handle
        type="target"
        position={Position.Top}
        className="w-3 h-3 bg-blue-500"
      />
      <div className="flex items-center gap-2 mb-1">
        <div className="w-8 h-8 bg-blue-100 rounded-lg
                        flex items-center justify-center text-lg">
          A
        </div>
        <div>
          <p className="font-semibold text-sm">{nodeData.label}</p>
          <p className="text-xs text-gray-500">{nodeData.model}</p>
        </div>
      </div>
      <Handle
        type="source"
        position={Position.Bottom}
        className="w-3 h-3 bg-blue-500"
      />
    </div>
  );
}

const nodeTypes = { agentNode: AgentNode };

The Handle components define connection points. target handles accept incoming edges, source handles start outgoing edges. Position them at the top and bottom for a top-to-bottom flow layout.

The Node Palette with Drag-and-Drop

A sidebar palette lets users drag new node types onto the canvas. Use React DnD or the native HTML drag API.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

const nodeTemplates = [
  { type: "agentNode", label: "Agent", icon: "A" },
  { type: "toolNode", label: "Tool", icon: "T" },
  { type: "conditionNode", label: "Condition", icon: "?" },
  { type: "outputNode", label: "Output", icon: "O" },
];

function NodePalette() {
  const onDragStart = (
    event: React.DragEvent,
    nodeType: string
  ) => {
    event.dataTransfer.setData(
      "application/reactflow",
      nodeType
    );
    event.dataTransfer.effectAllowed = "move";
  };

  return (
    <div className="w-48 border-r p-4 space-y-2">
      <h3 className="font-semibold text-sm mb-3">Components</h3>
      {nodeTemplates.map((tpl) => (
        <div
          key={tpl.type}
          draggable
          onDragStart={(e) => onDragStart(e, tpl.type)}
          className="flex items-center gap-2 p-2 border rounded-lg
                     cursor-grab hover:bg-gray-50"
        >
          <span className="w-7 h-7 bg-gray-100 rounded flex
                           items-center justify-center text-sm">
            {tpl.icon}
          </span>
          <span className="text-sm">{tpl.label}</span>
        </div>
      ))}
    </div>
  );
}

Drop Handler on the Canvas

When a node is dropped on the canvas, calculate its position relative to the React Flow viewport and add it to the nodes array.

import { useReactFlow } from "@xyflow/react";

function useDropHandler(
  setNodes: React.Dispatch<React.SetStateAction<Node[]>>
) {
  const { screenToFlowPosition } = useReactFlow();

  const onDrop = (event: React.DragEvent) => {
    event.preventDefault();
    const type = event.dataTransfer.getData("application/reactflow");
    if (!type) return;

    const position = screenToFlowPosition({
      x: event.clientX,
      y: event.clientY,
    });

    const newNode: Node = {
      id: crypto.randomUUID(),
      type,
      position,
      data: { label: `New ${type}`, model: "gpt-4o-mini" },
    };

    setNodes((nds) => [...nds, newNode]);
  };

  const onDragOver = (event: React.DragEvent) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = "move";
  };

  return { onDrop, onDragOver };
}

Serializing the Workflow

The workflow must be saved to a backend. Serialize the nodes and edges into a JSON structure that your agent runtime can interpret.

interface SerializedWorkflow {
  version: string;
  nodes: Array<{
    id: string;
    type: string;
    config: Record<string, unknown>;
  }>;
  edges: Array<{
    source: string;
    target: string;
    condition?: string;
  }>;
}

function serializeWorkflow(
  nodes: Node[],
  edges: Edge[]
): SerializedWorkflow {
  return {
    version: "1.0",
    nodes: nodes.map((n) => ({
      id: n.id,
      type: n.type || "agentNode",
      config: n.data as Record<string, unknown>,
    })),
    edges: edges.map((e) => ({
      source: e.source,
      target: e.target,
      condition: e.label as string | undefined,
    })),
  };
}

FAQ

How do I add a configuration panel that opens when a node is clicked?

Listen for the onNodeClick event on the ReactFlow component. Store the selected node ID in state and conditionally render a side panel with form fields for that node's configuration (model, system prompt, tools, temperature). Update the node's data in the nodes array when the form changes.

How do I validate the workflow before saving?

Check that all nodes have at least one incoming or outgoing edge (except the start node). Verify there are no cycles if your agent runtime does not support them. Ensure every condition edge has a non-empty label. Run these validations before serialization and highlight invalid nodes with a red border.

Can I undo and redo changes in the editor?

Yes. Maintain a history stack of { nodes, edges } snapshots. Push a new snapshot on every meaningful change (node add, delete, move, connect). Pop from the stack on undo. Use a separate redo stack that gets cleared when a new change is made after an undo.


#DragAndDrop #VisualEditor #ReactFlow #TypeScript #WorkflowBuilder #AgenticAI #LearnAI #AIEngineering

Share
C

Written by

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.

Related Articles You May Like

Learn Agentic AI

Building Your First MCP Server: Connect AI Agents to Any External Tool

Step-by-step tutorial on building an MCP server in TypeScript, registering tools and resources, handling requests, and connecting to Claude and other LLM clients.

Learn Agentic AI

How to Build an AI Coding Assistant with Claude and MCP: Step-by-Step Guide

Build a powerful AI coding assistant that reads files, runs tests, and fixes bugs using the Claude API and Model Context Protocol servers in TypeScript.

Learn Agentic AI

Building an Agent Playground: Interactive Testing Environment for Prompt and Tool Development

Build a full-featured agent playground with a web UI that lets you test prompts live, tune parameters, compare model outputs side by side, and export working configurations for production deployment.

Learn Agentic AI

Generative UI with AI Agents: Dynamically Creating React Components from Natural Language

Explore how the Vercel AI SDK's generativeUI capability lets AI agents stream fully interactive React components to users, replacing static text responses with dynamic, data-rich interfaces.

Learn Agentic AI

Building an Agent with Mastra Framework: TypeScript-First Agent Development

Learn how to build AI agents using the Mastra framework. This guide covers project setup, agent definition with typed tools, persistent memory, workflow orchestration, and deployment strategies for TypeScript-first agent applications.

Learn Agentic AI

Building an MCP Server in TypeScript: Node.js Tools for AI Agents

Create a fully typed MCP server in TypeScript using the official MCP SDK, with tool handlers, Zod validation, and deployment strategies for exposing Node.js services to AI agents.