This document describes the visual workflow editing system built on ReactFlow. It covers the canvas component structure, node rendering, drag-and-drop interactions, block positioning, and viewport management. For underlying workflow data structures and DAG execution model, see Workflow Fundamentals. For real-time multi-user synchronization, see Collaborative Editing.
The workflow canvas is implemented using the reactflow library, providing the foundation for visual node-graph editing. The main WorkflowContent component in workflow.tsx orchestrates the entire canvas and its associated stores.
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:3-43, apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:170-250
The canvas supports three primary node types, each mapped to a specific React component via the nodeTypes object:
| Node Type | Component | Block Types | Purpose |
|---|---|---|---|
workflowBlock | WorkflowBlock | All execution blocks (agent, api, function, etc.) | Standard workflow logic blocks |
noteBlock | NoteBlock | note | Canvas annotations and documentation |
subflowNode | SubflowNodeComponent | loop, parallel | Container blocks for nested execution |
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:182-193
The canvas uses specific configuration constants for consistent behavior, including snapping to grid and partial selection modes.
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:196-209, apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:218-225
The WorkflowBlock component renders standard execution blocks. It uses several hooks to manage its visual state and dimensions.
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx:1-56, apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx:851-925
WorkflowBlock renders a preview of subblock values. The system uses type guards to format complex data types (like messages or variable assignments) into human-readable strings for the canvas.
| Guard Function | Data Type | Formatting Behavior |
|---|---|---|
isMessagesArray | LLM Chat History | Counts messages (e.g., "3 messages") |
isVariableAssignmentsArray | Variable Updates | Counts assignments |
isTableRowArray | Table Data | Counts rows |
isFieldFormatArray | Schema Definitions | Lists field names |
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx:83-175, apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx:249-414
SubflowNodeComponent handles the rendering of containers. It supports nesting by calculating its level in the parent chain and adjusts its handles based on whether it is a loop or parallel block.
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/subflow-node.tsx:61-125
The canvas handles node movement through onNodeDragStop and onSelectionDragStop. It includes logic to detect if a node has been dropped into a container block (Loop/Parallel).
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:1135-1245, apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:664-757
Selection is synchronized with the PanelEditorStore via syncPanelWithSelection. This ensures that when a block is clicked on the canvas, its configuration appears in the side panel.
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:170-180, apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:1367-1420
When a new block is added, prepareBlockState initializes its internal subBlocks and outputs based on the block definition in the registry.
Sources: apps/sim/stores/workflows/utils.ts113-204
The WorkflowRegistry store manages a clipboard object. Pasting involves regenerating UUIDs via regenerateBlockIds and remapping edges to the new IDs. The calculatePasteOffset function ensures pasted blocks are centered in the viewport.
Sources: apps/sim/stores/workflows/registry/store.ts78-80 apps/sim/stores/workflows/utils.ts46-52 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:111-148, apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:967-1088
Dimensions are strictly controlled to ensure the canvas remains organized.
| Constant | Value | Description |
|---|---|---|
BLOCK_DIMENSIONS.FIXED_WIDTH | 250 | Standard width for all non-container blocks |
BLOCK_DIMENSIONS.HEADER_HEIGHT | 40 | Height of the block title bar |
CONTAINER_DIMENSIONS.DEFAULT_WIDTH | 400 | Initial width for Loop/Parallel containers |
CONTAINER_DIMENSIONS.TOP_PADDING | 16 | Internal spacing for nested nodes |
Sources: apps/sim/lib/workflows/blocks/block-dimensions.ts10-33
The useBlockDimensions hook calculates the height of a block dynamically based on the number of subblocks it is configured to display (up to a maximum of 3).
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-block-dimensions.ts:40-85
Edges are validated before creation to maintain the integrity of the workflow's Directed Acyclic Graph (DAG).
wouldCreateCycle to ensure no circular paths are created.isBlockProtected to prevent connecting to or from locked blocks.Sources: apps/sim/stores/workflows/workflow/utils.ts15-52 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx:1247-1365
Handles are rendered based on the block's outputs and subBlocks. For example, a "Router" block dynamically generates output handles based on the number of routes defined in its subblocks.
Sources: apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx:1085-1178, apps/sim/lib/workflows/dynamic-handle-topology.ts14-30
The WorkflowRegistry store manages the lifecycle of workflow metadata and state. It handles workspace transitions and resets stores to prevent data leakage.
Sources: apps/sim/stores/workflows/registry/store.ts36-70 apps/sim/stores/workflows/registry/store.ts81-121
Refresh this wiki