A simple node-based workflow builder where users can connect different task nodes and edges as conditions.
Right now it supports 3 distinctive nodes:
- Start - Mark the start task or trigger
- Task - Any intermediate task
- End - End task
The codebase uses a registry pattern with configurable nodes, so we can create different specialized nodes based on the use case.
π₯ Import sample JSON: Download phone-call workflow and load it using Import JSON option β workflow-2026-02-26T09-03-21-201Z.json
Live Demo: https://visual-condition-workflow-builder.vercel.app/
Dashboard PART 1 : https://www.loom.com/share/2aae81e0a3ca422ea9d8af39e83e9ac0 Dashboard PART 2 : https://www.loom.com/share/68cbc7d0b32f463da6e81aa591dd603c
- Workflow Builder:
/ - Design System:
/designsystem - UI Playground:
/_playground
You need a minimum Node.js version of 22.13.0 for development and build.
- Clone the repository π₯
HTTPS
git clone https://github.com/ashwaniarya/visual-condition-workflow-builder.gitSSH
git clone git@github.com:ashwaniarya/visual-condition-workflow-builder.git- Move into project root π
cd visual-worflow-builder-react- Check your current Node.js version π
node -v- Use
nvmonly if Node.js is lower than22.13.0π§©
nvm install 22.13.0
nvm use 22.13.0
node -v- Install dependencies π¦
npm install- Run development server
βΆοΈ
npm run dev- Create production build ποΈ
npm run build- Preview production build locally π
npm run previewNode Environment Required: 22.13.0
- Vite
| Library / Tool | Why this choice | Trade-off to keep in mind |
|---|---|---|
| ReactJS | Component composition makes the node-canvas UI easy to break into isolated, reusable building blocks (Palette, Canvas, Config, Viewer). |
Large interactive trees can re-render often if memoization boundaries are not designed carefully. |
| React Flow | Provides production-ready node/edge graph rendering, drag-and-drop interactions, zoom/pan controls, and connection lifecycle APIs that drive the core workflow canvas behavior. | Strong library conventions mean custom behavior should be designed around its extension points to avoid brittle overrides. |
| Redux + Redux Toolkit | Predictable centralized state for cross-screen concerns (selection, modal, toast, workflow status) with less boilerplate via slices and typed actions. | Adds indirection compared to local state, so state boundaries must stay intentional. |
| NPM | Native Node ecosystem support with straightforward scripts for local dev, build, and preview. | Lockfile discipline is required to keep team environments deterministic. |
| Tailwind CSS | Fast utility-first styling helps iterate on canvas-heavy UI without context switching between component and stylesheet files. | Utility classes can become noisy if design tokens and composition conventions are not enforced. |
| shadcn | Copy-as-code component primitives provide full control over styling and behavior, useful for product-specific workflow UI customization. | Component ownership shifts to this repo, so upgrades are manual instead of package-driven. |
| Zod | Runtime schema validation protects workflow import/export boundaries and keeps JSON contracts explicit and type-safe. | Schemas need ongoing maintenance as domain models evolve. |
Other dependencies are provided in the package.json file.
-
Node and Edge interface design
- Node
- Generic node interface that can be further specialized.
- Tracks whether a node is configured or not.
- Defines input and output port definitions.
- Stores basic UI-level details.
- Edge
- Generic base edge interface that can be further specialized.
- Node
-
Edge and Node Registry
- Acts as a central state where all nodes can be created and used consistently.
-
Component design
- Components use composition with separation of concerns to increase reusability.
-
Custom Hooks
- Business logic is moved into hooks so only targeted parts need to change.
-
Performance optimization
- Controlled reactivity using debouncing.
- Controlled rendering of
ConfigurationPanel. - Code splitting with lazy-loaded feature modules to reduce initial bundle load.
-
Centralized constants
- Values come from constants, making updates easy.
- This also enables future language-specific extensions.
-
JSON import validation
- Built-in validation checks incoming data shape.
- Can be extended later for checksum-based verification.
-
UI quality
- Clean modern UI with context-based interaction.
A simple high-level presentation view of how layers collaborate across UI rendering, state ownership, and workflow domain transforms.
flowchart TB
subgraph entryLayer [EntryAndLayoutLayer]
appRouter[router.tsx]
workflowScreen[WorkflowScreen]
appRouter --> workflowScreen
end
subgraph uiLayer [UiCompositionLayer]
workflowHeader[WorkFlowHeader]
nodePalette[NodePalette]
canvasContainer[CanvasContainer]
configurationPanel[ConfigurationPanel]
workflowViewer[WorkflowViewer]
validationViewer[WorkFlowValidation]
jsonViewer[WorkFlowJsonViewer]
modalHost[GlobalModalHost]
toastHost[GlobalToastHost]
end
subgraph hookLayer [InteractionHooksLayer]
canvasSelectionHook[useCanvasSelection]
canvasDragHook[useCanvasDrag]
canvasConnectHook[useCanvasConnect]
workflowValidationHook[useWorkflowValidation]
canvasEventBus[canvasEventBus]
end
subgraph stateLayer [StateOwnershipLayer]
reactFlowState[ReactFlowStateNodesEdges]
canvasSliceState[ReduxCanvasSliceSelectionValidation]
globalUiSliceState[ReduxGlobalUiSliceModalToast]
end
subgraph domainLayer [WorkflowDomainLayer]
registry[NodeRegistryTemplates]
mapper[WorkflowFlowMapper]
parser[ParseWorkflowJson]
end
subgraph sharedLayer [SharedUtilitiesLayer]
validator[computeWorkflowStateAndMessage]
end
workflowScreen --> workflowHeader
workflowScreen --> nodePalette
workflowScreen --> canvasContainer
workflowScreen --> configurationPanel
workflowScreen --> workflowViewer
workflowScreen --> modalHost
workflowScreen --> toastHost
canvasContainer --> canvasSelectionHook
canvasContainer --> canvasDragHook
canvasContainer --> canvasConnectHook
canvasContainer --> workflowValidationHook
canvasContainer --> canvasEventBus
canvasContainer -->|"owns graph state"| reactFlowState
canvasSelectionHook -->|"dispatch selection updates"| canvasSliceState
workflowValidationHook -->|"dispatch workflow validation"| canvasSliceState
canvasSelectionHook <-->|"emit and subscribe"| canvasEventBus
canvasDragHook <-->|"emit and subscribe"| canvasEventBus
canvasConnectHook <-->|"emit and subscribe"| canvasEventBus
configurationPanel -->|"reads selection"| canvasSliceState
validationViewer -->|"reads workflow status"| canvasSliceState
jsonViewer -->|"reads graph snapshot"| reactFlowState
workflowHeader -->|"dispatch modal and toast actions"| globalUiSliceState
modalHost -->|"reads active modal"| globalUiSliceState
toastHost -->|"reads toast event"| globalUiSliceState
nodePalette -->|"creates nodes via drag flow"| canvasDragHook
canvasDragHook -->|"uses node templates"| registry
workflowHeader -->|"export maps flow to payload"| mapper
workflowHeader -->|"import parses payload"| parser
workflowHeader -->|"import maps payload to flow"| mapper
mapper --> reactFlowState
workflowValidationHook -->|"computes validation state"| validator
Layerπ§±: grouped responsibility boundary (subgraph).owns graph stateποΈ: ReactFlow local nodes and edges are the canvas source of truth.dispatch ... updatesβ‘: writes into Redux state.reads ...π: selector based consumption from Redux or ReactFlow state.emit and subscribeπ‘: canvas hooks coordinate through a shared event bus.maps / parsesπ: domain transformation between graph state and portable workflow JSON payload.computes validation stateβ : shared utility derives workflow validity from current graph.
- User interacts with
NodePaletteorCanvasContainerπ― - Hooks process intent and update
ReactFlowStateor Redux βοΈ - UI panels (
ConfigurationPanel,WorkFlowValidation,WorkFlowJsonViewer) render derived state πͺ WorkFlowHeaderdrives import and export through parser and mapper π- Global feedback (
GlobalModalHost,GlobalToastHost) is centralized via global UI slice π£
High Level folder structure
visual-worflow-builder-react/
ββ src/
β ββ entry/
β β ββ App.tsx
β β ββ router.tsx
β ββ presentation/
β β ββ components/
β β β ββ edges/
β β β ββ modals/
β β β ββ nodes/
β β β β ββ canvas/
β β β β ββ configuration/
β β β β β ββ primitives/
β β β β ββ palette/
β β β ββ toast/
β β β ββ ConfigurationPanel.tsx
β β β ββ NodePalette.tsx
β β β ββ WorkFlowHeader.tsx
β β β ββ WorkFlowJsonViewer.tsx
β β β ββ WorkFlowValidation.tsx
β β β ββ WorkflowViewer.tsx
β β ββ screens/
β β ββ DesignSystemScreen.tsx
β β ββ UITestPlaygroundScreen.tsx
β β ββ WorkflowScreen.tsx
β ββ interaction/
β β ββ canvas/
β β β ββ events/
β β β ββ hooks/
β β β ββ CanvasContainer.tsx
β β ββ hooks/
β ββ state/
β β ββ store/
β ββ domain/
β β ββ model/
β β ββ registry/
β β ββ workflow/
β β ββ constants/
β β ββ io/
β β ββ mapping/
β β ββ parser/
β β ββ schema/
β β ββ serialization/
β β ββ index.ts
β ββ design-system/
β β ββ ui/
β β ββ atoms/
β β ββ components/
β β ββ internal/
β β ββ animate-ui/
β β ββ components/
β β ββ primitives/
β ββ shared/
β β ββ constants/
β β ββ lib/
β β ββ utils/
β ββ modal/
β ββ utils/
β ββ index.css
β ββ main.tsx
ββ components.json
ββ package.json
ββ readme.md
For details context of folder structure move to.
Canonical map (for humans + AI): docs/folder-and-file-map.md
A minimal pub-sub for canvas UI events. Handlers emit; hooks subscribe and react. No return values.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ReactFlow (onDrop, onClick, onConnect, onDelete, β¦) β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β emitCanvasEvent(type, payload)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β canvasEventBus (singleton) β
β emitCanvasEvent() β subscribeCanvasEvent() β unsubscribe β
ββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββΌββββββββββββββββββββ
βΌ βΌ βΌ
useCanvasDrag useCanvasConnect useCanvasSelection
setNodes setEdges dispatch(selection)
- Decouple UI actions β Drag, mode, delete, input change, connect stay in separate hooks.
- Single source of truth β One bus; all canvas events flow through it.
- Extensible β Add new event types and subscribers without touching existing hooks.
| Without event bus | With event bus |
|---|---|
| God component with all handlers | Per-concern hooks (drag, selection, connect) |
| Tight coupling, hard to test | Loose coupling, easy to mock emit |
| Undo/redo = invasive refactor | Future command pattern can subscribe to same events |