4 April 2026

React Flow Architecture: How to Structure a Workflow Editor with MVC (Part 2)

This is the second part of the series on building a workflow editor with React Flow.

Mini Map

In this part, you will learn how to apply a Model View Controller architecture to keep the workflow state, its visual representation, and the logic that connects them in separate layers.

Model View Controller

A good starting point is to think about the workflow functionality in terms of a Model View Controller architecture, where the workflow state, its visual representation, and the logic that connects them live in separate layers. The diagram below illustrates how the core types and functions interact.

Architecture diagram showing the MVC data flow in Workflow Kit. The Workflow model (top-left) transforms into Flows via workflowView() (top-left to bottom-left). User interactions on Flows produce FlowsChange[] via onFlowsChange() (bottom-left to bottom-right). FlowsChange[] is converted to WorkflowChange[] via toWorkflowChanges() (bottom-right to top-right). Finally, WorkflowChange[] is applied back to Workflow via applyWorkflowChanges() (top-right to top-left), completing the cycle.

Types:

  • Workflow: The current workflow state (Model).
  • Flows: The visual representation of the workflow (View).
  • FlowsChange: An event captured from the visual representation (select, drag, or drop).
  • WorkflowChange: A change that occurred in the workflow.

Functions:

  • workflowView: Transforms a Workflow into its visual Flows representation.
  • onFlowsChange: Triggered whenever the user interacts with the Flows by selecting, dragging, or dropping a node, producing FlowsChange objects.
  • toWorkflowChanges: Converts FlowsChange[] into WorkflowChange[], acting as the Controller that bridges the View and the Model.
  • applyWorkflowChanges: Applies the provided WorkflowChange[] against the current workflow to produce an updated Workflow.

Implementation

To implement this architecture we can have a useState hook that holds the workflow state and pass it down to the WorkflowView component. Whenever the workflow needs to update, such as when adding, removing or modifying elements, the onWorkflowChange function gets called to apply those changes.

function initialWorkflow(): Workflow {
  return {
    flow: {
      start: { message: "" },
      elements: [],
      end: { message: "" },
    },
    widget: null,
    active: null,
  };
}

export default function Home() {
  const [workflow, setWorkflow] = useState(() => initialWorkflow());

  const onWorkflowChange = useCallback((changes: WorkflowChange[]) => {
    setWorkflow((workflow) => applyWorkflowChanges(changes, workflow));
  }, []);

  return (
    <div className="flex h-screen flex-col bg-neutral-900">
      <WorkflowView workflow={workflow} onWorkflowChange={onWorkflowChange} />
    </div>
  );
}

In WorkflowView, the workflow prop is the model that holds the workflow state, workflowView is the view that produces the visual representation from it, and toWorkflowChanges is the controller that captures user interactions and converts them into workflow changes.

interface WorkflowViewProps {
  workflow: Workflow;
  onWorkflowChange: OnWorkflowChange;
}

export function WorkflowView({
  workflow,
  onWorkflowChange,
}: WorkflowViewProps) {
  const flows = useMemo(
    () => workflowView(workflow, onWorkflowChange),
    [workflow, onWorkflowChange],
  );

  const onFlowsChange = useCallback(
    (changes: TypedFlowsChange[]) => {
      const array = toWorkflowChanges(changes, workflow, flows);
      if (array.length) onWorkflowChange(array);
    },
    [onWorkflowChange, workflow, flows],
  );

  return (
    <>
      <FlowsView
        flows={flows}
        onFlowsChange={onFlowsChange}
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
      />
      {widgetView(workflow, onWorkflowChange)}
    </>
  );
}

The widgetView function is also worth noting, as it renders a sidebar that can be used to show information about the selected element and allows for additional interactions such as modifying or adding elements to the workflow.

Explore the Code

A reduced version of this Model View Controller architecture can be found here.

Next Steps

The next part covers how to extend the workflow editor with drag and drop support, allowing users to add and rearrange elements on the canvas in a more natural and intuitive way.

Frequently Asked Questions

React Flow Architecture: How to Structure a Workflow Editor with MVC (Part 2)