Overview
FlowKit is a powerful visual workflow builder and execution engine for the browser. Create, edit, test, and run automation workflows through an intuitive drag-and-drop interface with real-time execution feedback.
Key Features
- 70+ built-in node types - Triggers, actions, conditions, loops, HTTP, email, Slack, AWS, and more
- 15 specialized executors - Each node type has a dedicated executor with validation and configuration
- Real-time execution - Watch workflows run with visual state indicators (pending, running, success, error)
- Variable interpolation - Use
{{lastResult.data}}syntax to pass data between nodes - Full editing suite - Undo/redo (50 levels), copy/paste, multi-select, duplicate, group nodes
- Navigation aids - Minimap, zoom controls, fit-to-view, pan with space+drag
- Multiple exports - JSON (for import), Mermaid (for docs), PNG image (for sharing)
- Customizable canvas - Dot/grid/line patterns, light/dark themes, snap-to-grid, animated edges
- Condition branch colors - Green for true path, red for false path on condition nodes
FlowKit works in all modern browsers (Chrome, Firefox, Safari, Edge). No server required - runs entirely in the browser.
Installation
NPM
npm install flowkit
CDN
<script src="https://unpkg.com/flowkit/dist/flowkit.umd.js"></script>
ES Module
import { WorkflowBuilder, WorkflowExecutor } from 'flowkit';
Quick Start
import { WorkflowBuilder } from 'flowkit';
// Initialize builder
const container = document.getElementById('app');
const builder = new WorkflowBuilder(container, {
theme: 'light',
background: 'dots',
minimap: true
});
// Add nodes
builder.addNode('trigger', 100, 100);
builder.addNode('action', 300, 100);
builder.addNode('end', 500, 100);
// Connect nodes
builder.connect('1', '2');
builder.connect('2', '3');
// Execute workflow
await builder.runWorkflow();
WorkflowBuilder
The main class for creating and managing visual workflows.
Constructor Options
| Option | Type | Default | Description |
|---|---|---|---|
theme |
string | light | Color theme: 'light' or 'dark' |
mode |
string | edit | Mode: 'edit' (full editing) or 'view' (read-only) |
nodes |
array | ['trigger', 'action', ...] | Array of node type names to enable |
background |
string | dots | Canvas pattern: 'none', 'dots', 'lines', 'grid' |
edgeType |
string | bezier | Connection style: 'bezier', 'straight', 'step' |
minimap |
boolean | true | Show minimap navigation panel |
snapToGrid |
boolean | false | Snap nodes to grid when dragging |
gridSize |
number | 20 | Grid size in pixels for snapping |
animatedEdges |
boolean | false | Animate edges with flowing dots |
showArrows |
boolean | true | Show arrow markers on connection ends |
customNodes |
object | {} | Custom node type definitions |
onSave |
function | null | Callback when workflow is saved |
onChange |
function | null | Callback fired on any workflow change |
Example with All Options
const builder = new WorkflowBuilder('#app', {
theme: 'dark',
mode: 'edit',
nodes: ['trigger', 'action', 'condition', 'email', 'http'],
background: 'grid',
edgeType: 'bezier',
minimap: true,
snapToGrid: true,
gridSize: 20,
animatedEdges: true,
showArrows: true,
customNodes: {
myNode: {
label: 'My Custom Node',
icon: '⭐',
category: 'custom',
inputs: 1,
outputs: 1,
fields: []
}
},
onSave: (data) => console.log('Saved:', data),
onChange: (data) => console.log('Changed:', data)
});
Methods
| Method | Description |
|---|---|
addNode(type, x, y) | Add a node at position. Returns node ID. |
removeNode(nodeId) | Remove a node by ID. |
getNode(nodeId) | Get node data by ID. |
getNodes() | Get all nodes as array. |
connect(fromId, toId) | Create connection between two nodes. |
registerNode(name, def) | Register a custom node type. |
| Method | Description |
|---|---|
undo() | Undo last action |
redo() | Redo undone action |
clear() | Clear entire workflow |
copySelected() | Copy selected nodes |
paste() | Paste copied nodes |
deleteSelected() | Delete selected nodes |
zoomIn() / zoomOut() | Change zoom level |
fitView() | Fit all nodes in view |
groupSelected() | Group selected nodes together |
ungroupSelected() | Ungroup selected nodes |
setTheme(theme) | Set 'light' or 'dark' theme |
setMode(mode) | Set 'edit' or 'view' mode |
toggleMinimap() | Show/hide minimap panel |
toggleSnap() | Toggle snap to grid |
| Method | Description |
|---|---|
runWorkflow() | Execute entire workflow |
runFromNode(nodeId) | Run from specific node |
runSingleNode(nodeId) | Test single node |
pauseWorkflow() | Pause execution |
stopWorkflow() | Stop execution |
resetWorkflow() | Reset execution state |
getExecutionState() | Export execution state for persistence |
setExecutionState(state) | Restore execution state |
getExecutionLog() | Get execution history array |
getExecutionContext() | Get data passed between nodes |
| Method | Description |
|---|---|
export() | Returns workflow data object |
import(data) | Import workflow from data |
exportWorkflow() | Download as JSON file |
exportAsImage() | Export as PNG image |
validate() | Validate workflow structure |
| Method | Description |
|---|---|
setNodeColor(nodeId, color) | Set color of a specific node |
setConnectionColor(from, to, color) | Set color of a specific connection |
setDefaultConnectionColor(color) | Set default color for all connections |
setConditionColors(true, false) | Set colors for condition branches |
Node Types
70+ built-in node types organized into categories. Each node can be dragged from the sidebar onto the canvas.
Core Nodes
Integration Nodes
Condition Node (Special)
Condition nodes have two outputs with automatic color coding:
- Output 1 (True/Yes): Green - followed when condition evaluates to true
- Output 2 (False/No): Red - followed when condition evaluates to false
The output dots and connection lines are automatically colored to visually indicate the branch direction.
{{lastResult.data.status}} == 'active'
{{lastResult.data.count}} > 10
{{lastResult.data.items}}.length > 0
Executors
Each node type is handled by a specialized executor with specific configuration options. Executors process nodes during workflow execution.
| Executor | Node Types | Description |
|---|---|---|
| TriggerExecutor | trigger, start, schedule, webhook | Workflow entry points. Sets initial variables and context. |
| ActionExecutor | action, process, task | General actions: HTTP calls, code execution, logging, set variables. |
| ConditionExecutor | condition, if, decision, switch | Branching logic. Evaluates expressions and routes to output_1 (true) or output_2 (false). |
| LoopExecutor | loop, foreach, while | Iterate over arrays. Provides {{item}} and {{index}} variables. |
| TransformExecutor | transform, map, merge, split | Data transformation: map, filter, reduce, merge objects, split strings. |
| FilterExecutor | filter | Filter arrays based on conditions. |
| DelayExecutor | delay, wait, sleep | Pause execution for specified duration. |
| HttpExecutor | http, api, request | Make HTTP/HTTPS requests. Supports all methods, headers, body, auth. |
| EmailExecutor | email, sendmail | Send emails with to, subject, body, attachments. |
| DatabaseExecutor | database, sql, query | Execute database queries (simulated in browser). |
| SlackExecutor | slack, notification | Send Slack messages to channels. |
| AwsExecutor | aws, s3, lambda | AWS service calls (S3, Lambda, SQS). |
| DockerExecutor | docker, container | Docker operations (simulated). |
| CodeExecutor | code, script, function | Execute JavaScript code with access to context. |
| EndExecutor | end, stop, terminate | Workflow termination. Returns final result. |
Executor Result Structure
Every executor returns a consistent result object:
{
success: true, // Whether execution succeeded
data: { ... }, // Result data (varies by executor)
output: 'output_1', // Which output to follow (for conditions)
error: null, // Error message if failed
nextNodes: ['2'] // IDs of next nodes to execute
}
Custom Executor
Register a custom executor for your node type:
// Register custom executor
builder.registerExecutor('myNode', async (node, context) => {
const config = node.data;
// Access previous results
const lastResult = context.variables.lastResult;
// Do work...
const result = await myCustomLogic(config.option);
return {
success: true,
data: result
};
});
Keyboard Shortcuts
Variable Interpolation
Use {{variable}} syntax to insert dynamic values into any field.
| Variable | Description |
|---|---|
{{lastResult}} | Full result of previous node |
{{lastResult.data}} | Data from previous node |
{{lastResult.data.field}} | Specific field from result |
{{node_1}} | Result of node with ID 1 |
{{item}} | Current item in loop |
{{index}} | Current index in loop |
Examples
// In HTTP URL
"https://api.example.com/users/{{lastResult.data.id}}"
// In condition
"{{lastResult.data.status}} == 'active'"
// In email body
"Hello {{lastResult.data.name}}, your order is ready."
Custom Nodes
Register a Custom Node
builder.registerNode('myNode', {
label: 'My Custom Node',
icon: '<svg>...</svg>',
color: '#FF5733',
inputs: 1,
outputs: 2,
fields: [
{ name: 'option', label: 'Option', type: 'select',
options: [
{ value: 'a', label: 'Option A' },
{ value: 'b', label: 'Option B' }
]
}
]
});
Field Types
text | Single line text input |
textarea | Multi-line text |
number | Numeric input |
select | Dropdown with options |
checkbox | Boolean toggle |
json | JSON editor |
code | Code editor |
Connection Styling
Customize the appearance of connections and nodes programmatically.
Default Connection Color
Set the default color for all connections:
// Set default connection color (teal)
builder.setDefaultConnectionColor('#02514a');
// Set to custom color
builder.setDefaultConnectionColor('#FF5733');
Individual Connection Color
Change color of a specific connection:
// Set connection color between node 1 and node 2
builder.setConnectionColor(1, 2, '#8B5CF6');
// With specific output/input classes
builder.setConnectionColor(1, 2, '#EC4899', 'output_1', 'input_1');
Condition Node Colors
Condition nodes (condition, if, decision) have two outputs with automatic coloring:
- Output 1 (True/Success): Green (#22C55E) by default
- Output 2 (False/Error): Red (#EF4444) by default
Customize condition branch colors:
// Set custom colors for condition branches
builder.setConditionColors('#10B981', '#F97316');
// First param: true/success color (output_1)
// Second param: false/error color (output_2)
Node Color
Change the color of individual nodes:
// Set node color
builder.setNodeColor(nodeId, '#FF5733');
true, execution follows the green output (output_1).
When it evaluates to false, execution follows the red output (output_2).
The output dots and connection lines are automatically colored to indicate the branch direction.
Events
Listen to workflow events for integration with your application.
Using Callbacks
const builder = new WorkflowBuilder('#app', {
onChange: (data) => {
console.log('Workflow changed:', data);
// Auto-save, validation, etc.
},
onSave: (data) => {
console.log('Workflow saved:', data);
// Send to server, show notification, etc.
}
});
Available Events
| Event | Description | Data |
|---|---|---|
onChange | Fired on any workflow change | Full workflow data object |
onSave | Fired when Ctrl+S or Save clicked | Full workflow data object |
nodeCreated | When a node is added | Node ID and type |
nodeRemoved | When a node is deleted | Node ID |
connectionCreated | When nodes are connected | Source and target IDs |
Drawflow Events
Access underlying Drawflow events for advanced control:
// Access Drawflow instance
builder.drawflow.on('nodeCreated', (id) => {
console.log('Node created:', id);
});
builder.drawflow.on('connectionCreated', ({ output_id, input_id }) => {
console.log('Connected:', output_id, '->', input_id);
});
builder.drawflow.on('nodeMoved', (id) => {
console.log('Node moved:', id);
});
Examples
Click Examples dropdown in VizFlow to load any of these pre-built workflows.
1. API Request Test
What it does: Makes one API call and transforms the response.
Use case: Quick API endpoint testing, validating response format.
Flow: Trigger → HTTP → Transform → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Trigger | Click "Run" to start |
| 2 | HTTP | url: https://jsonplaceholder.typicode.com/todos/1 |
| 3 | Transform | return { title: data.title, completed: data.completed }; |
// Expected output:
{ "title": "delectus aut autem", "completed": false }
2. Data Pipeline
What it does: Fetches users, filters invalid entries, transforms to clean format.
Use case: Clean contact lists from CRM API for mailing list.
Flow: Trigger → HTTP → Filter → Transform → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Trigger | Manual start |
| 2 | HTTP | url: https://jsonplaceholder.typicode.com/users |
| 3 | Filter | return item.address && item.address.city; |
| 4 | Transform | return data.map(u => ({ name: u.name, email: u.email, city: u.address.city })); |
3. Condition Flow
What it does: Routes workflow differently based on API response data.
Use case: Process tickets by status, route orders by type.
Flow: Trigger → HTTP → Condition → (True: Action A | False: Action B) → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Trigger | Manual start |
| 2 | HTTP | url: https://jsonplaceholder.typicode.com/todos/1 |
| 3 | Condition | conditionType: javascriptjs_condition: return ctx.lastResult?.data?.completed === true; |
| 4a | Action (True) | output_1 → Task completed handler |
| 4b | Action (False) | output_2 → Task incomplete handler |
4. Loop Example
What it does: Iterates over array, processing each item individually.
Use case: Process batch orders, send individual emails to list.
Flow: Trigger → Loop → Transform → (back to Loop) → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Trigger | data: { items: [1, 2, 3, 4, 5] } |
| 2 | Loop | source: {{lastResult.data.items}} |
| 3 | Transform | return { value: ctx.item * 2, original: ctx.item }; |
// Inside loop, access:
ctx.item // Current item (e.g., 1, 2, 3...)
ctx.index // Current index (0, 1, 2...)
5. Notification Flow
What it does: Sends notifications via Email AND Slack simultaneously.
Use case: Alert multiple channels on new order, system events.
Flow: Trigger → HTTP → (Email + Slack) → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Trigger | Manual or webhook |
| 2 | HTTP | Fetch data to notify about |
| 3a | to: user@example.comsubject: New: {{lastResult.data.title}} | |
| 3b | Slack | channel: #notificationsmessage: {{lastResult.data.title}} |
6. Error Handling
What it does: Demonstrates graceful handling of API failures.
Use case: Alert when services are down, retry logic.
Flow: Trigger → HTTP → (Success: Handler | Error: Alert) → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Trigger | Manual start |
| 2 | HTTP | output_1: Success (2xx)output_2: Error (4xx, 5xx, timeout) |
| 3a | Success | Process successful response |
| 3b | Error | Send alert, log error |
// Error result structure:
{ "error": "500 Internal Server Error", "ok": false, "status": 500 }
7. Full API Workflow
What it does: Complete API integration with auth, validation, filtering.
Use case: Build user dashboard with filtered content.
Flow: Trigger → HTTP → Condition → HTTP → Transform → Filter → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Trigger | data: { userId: 1 } |
| 2 | HTTP | url: /users/{{data.userId}}headers: {"Authorization": "Bearer token"} |
| 3 | Condition | field: company.name, operator: not_empty |
| 4 | HTTP | url: /posts?userId={{lastResult.data.id}} |
| 5 | Filter | return item.title.length > 20; |
8. Data Enrichment
What it does: For each user, fetch posts AND todos, merge together.
Use case: Build complete customer profiles from microservices.
Flow: Trigger → HTTP → Loop → HTTP → Transform → End
| Step | Node | Configuration |
|---|---|---|
| 1 | HTTP | Fetch list of users |
| 2 | Loop | source: {{lastResult.data}} |
| 3 | HTTP | url: /posts?userId={{item.id}} |
| 4 | Transform | return { user: ctx.item, posts: data }; |
9. Multi-Channel Alert
What it does: Monitors API health, sends alerts via Email, Slack, PagerDuty.
Use case: Production monitoring, uptime checks.
Flow: Schedule → HTTP → Condition → (OK: Log | Down: Alert → PagerDuty) → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Schedule | cronExpression: */5 * * * * (every 5 min) |
| 2 | HTTP | url: /healthtimeout_seconds: 5 |
| 3 | Condition | field: status, operator: equals, value: healthy |
| 4 | Alert | to: ops-team@example.comsubject: API Down! |
10. ETL Pipeline
What it does: Extract-Transform-Load: fetch data, process, save to database.
Use case: Nightly data sync between systems.
Flow: Schedule → HTTP → Filter → Transform → Loop → Database → End
| Step | Node | Configuration |
|---|---|---|
| 1 | Schedule | cronExpression: 0 2 * * * (daily 2 AM) |
| 2 | HTTP | Fetch source data |
| 3 | Filter | return item.userId <= 2; |
| 4 | Transform | return data.map(p => ({ ...p, title: p.title.toUpperCase() })); |
| 5 | Loop | Process each item |
| 6 | Database | operation: insert, table: posts, data: {{item}} |
Context Variables
These variables are available in all node configurations:
| Variable | Description | Example |
|---|---|---|
ctx | Full execution context | ctx.lastResult |
ctx.lastResult | Previous node result | ctx.lastResult.data |
ctx.node_N | Specific node by ID | ctx.node_2.data |
data | Shortcut for lastResult.data | data.map(...) |
ctx.item | Current loop item | ctx.item.id |
ctx.index | Current loop index (0-based) | ctx.index + 1 |
Interpolation Syntax
// Use {{}} in string fields:
url: 'https://api.example.com/users/{{data.userId}}'
message: 'Processing item {{item.name}} at index {{index}}'
// Use direct JS in code fields:
return data.filter(x => x.active);