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.
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.](/md/mvc.png)
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 aWorkflowinto its visualFlowsrepresentation.onFlowsChange: Triggered whenever the user interacts with theFlowsby selecting, dragging, or dropping a node, producingFlowsChangeobjects.toWorkflowChanges: ConvertsFlowsChange[]intoWorkflowChange[], acting as the Controller that bridges the View and the Model.applyWorkflowChanges: Applies the providedWorkflowChange[]against the current workflow to produce an updatedWorkflow.
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.