Documents
Home>Documents>AI>Agent>Xgen

Building a Drag-and-Drop Node System with React Flow

6 min readFeb 1, 2025Feb 22, 2026

The Birth of the Canvas Editor

The heart of XGen is its visual canvas editor. It needs to let users design AI workflows without writing a single line of code, which made an intuitive drag-and-drop node system a hard requirement.

Adopting React Flow

We evaluated several canvas libraries before making a decision.

LibraryProsCons
React FlowReact-native, custom node supportLearning curve
JointJSPowerful diagramming featuresHeavy, licensing
Rete.jsPurpose-built for node editorsSmall ecosystem

We ultimately went with React Flow. The deciding factor was the ability to build custom nodes freely as standard React components.

Basic Canvas Structure

import { ReactFlow, Background, Controls, MiniMap } from '@xyflow/react';

const CanvasEditor = () => {
  const { nodes, edges, onNodesChange, onEdgesChange, onConnect } = useCanvasStore();

  return (
    <div className="w-full h-full">
      <ReactFlow
        nodes={nodes}
        edges={edges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        nodeTypes={customNodeTypes}
        fitView
      >
        <Background variant="dots" gap={16} />
        <Controls />
        <MiniMap />
      </ReactFlow>
    </div>
  );
};

Custom Node System

Each AI component is represented as a node. Node types such as ChatOpenAI, VectorStore, and Retriever each have their own distinct UI and configuration options.

// Node type definitions
const customNodeTypes = {
  chatOpenAI: ChatOpenAINode,
  chatAnthropic: ChatAnthropicNode,
  vectorStore: VectorStoreNode,
  retriever: RetrieverNode,
  agent: AgentNode,
  tool: ToolNode,
};

Implementing Drag-and-Drop

We implemented a flow where dragging a node from the sidebar and dropping it onto the canvas creates a new node.

const onDrop = useCallback(
  (event: React.DragEvent) => {
    event.preventDefault();
    const type = event.dataTransfer.getData('application/reactflow');
    const position = screenToFlowPosition({
      x: event.clientX,
      y: event.clientY,
    });
    
    const newNode = {
      id: `${type}_${Date.now()}`,
      type,
      position,
      data: getDefaultNodeData(type),
    };
    
    addNode(newNode);
  },
  [screenToFlowPosition, addNode]
);

Edge Connection Rules

Not every node should be allowed to connect to every other node. We added validation logic to permit connections only when the output type and input type are compatible. This turned out to be more complex than expected, and getting the type system design right was critical.

Tags
React FlowCanvas Editordrag-and-dropnode systemXGen