
Drawflow is a dependency-free JavaScript library for building editable node-based SVG flowcharts and data-flow editors in the browser.
You work on a canvas-style editor where you can drag nodes, connect inputs and outputs, and edit data stored inside each node.
Drawflow runs in plain JavaScript and stores the complete flow as JSON for import and export. Registered nodes can also render Vue 2 or Vue 3 components.
Features:
- Drag-and-drop nodes with multiple inputs and outputs.
- Multiple connections between nodes.
- Node and connection deletion.
- Dynamic input and output management.
- Reroute points for connection lines.
- Node data synchronization through
df-*attributes. - Zoom controls and canvas translation.
- Edit, fixed, and view modes.
- Multiple flow modules.
- JSON import and export.
- Mouse, touch, keyboard, and pinch-zoom controls.
- Vue 2, Vue 3, and Nuxt integration.
Install & Download:
NPM
Install Drawflow from npm and import both the JavaScript module and stylesheet.
npm install drawflow
import Drawflow from 'drawflow'; import 'drawflow/dist/drawflow.min.css';
Browser CDN
You can also load the stylesheet and global JavaScript build from a CDN.
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/drawflow.min.css" /> <script src="https://unpkg.com/[email protected]/dist/drawflow.min.js"></script>
How to use it:
1. Create the editor container
Create an empty DIV container with a fixed height.
<div id="drawflow"></div>
<style>
#drawflow {
position: relative;
width: 100%;
height: 500px;
}
</style>2. Initialize Drawflow
Create the editor instance from the container, then call start().
const canvas = document.getElementById('drawflow');
const editor = new Drawflow(canvas);
editor.start();3. Add nodes
addNode() accepts the node name, input and output counts, canvas position, CSS class, data object, HTML content, and optional node type. Add df-* attributes to inputs, textareas, selects, or contenteditable elements when their values should sync with node data. Nested keys can use multiple levels such as df-user-name.
const nodeData = {
title: 'Profile'
};
const nodeHtml = `
<div>
<label>Title</label>
<input type="text" df-title placeholder="Node title">
</div>
`;
editor.addNode(
'profile',
0,
1,
120,
160,
'profile-node',
nodeData,
nodeHtml,
false
);4. Register reusable nodes
Register a DOM element once when the same node markup appears in multiple places.
const taskNode = document.createElement('div');
taskNode.innerHTML = '<strong>Task</strong>';
editor.registerNode('taskCard', taskNode);
editor.addNode('task', 1, 1, 320, 180, 'task-node', {}, 'taskCard', true);5. Export and import flow data
Use JSON-compatible data to save the current flow and restore it later.
const savedFlow = editor.export(); editor.import(savedFlow);
6. Work with modules
Modules store multiple flows inside one editor instance. The default module is Home.
editor.addModule('Review');
editor.changeModule('Review');
editor.removeModule('Review');
editor.changeModule('Home');7. Change editor modes
edit is the default mode. fixed locks node positions while keeping node fields available. view provides a read-only view.
editor.editor_mode = 'edit'; editor.editor_mode = 'fixed'; editor.editor_mode = 'view';
Mouse, Touch & Keyboard Controls:
- Press Delete to remove the selected node or connection.
- Right-click a selected item to show its remove control.
- Long-press on mobile for the remove action.
- Drag with the left mouse button to move nodes or the editor canvas.
- Hold Ctrl while using the mouse wheel to zoom.
- Use pinch gestures to zoom on touch devices.
Editor Options:
Set reroute before start() or import() when reroute points should appear during initial rendering. Other editor properties can be assigned directly on the instance.
reroute(Boolean, defaultfalse) activates reroute points on connections.reroute_fix_curvature(Boolean, defaultfalse) applies fixed curvature around reroute points.curvature(Number, default0.5) sets connection curvature.reroute_curvature_start_end(Number, default0.5) sets curvature for the first and last reroute segments.reroute_curvature(Number, default0.5) sets curvature between reroute points.reroute_width(Number, default6) sets the reroute point width.line_path(Number, default5) sets the connection line width.force_first_input(Boolean, defaultfalse) sends a dropped connection to the first input on the node.editor_mode(String, defaultedit) acceptsedit,fixed, orview.zoom(Number, default1) sets the current zoom level.zoom_max(Number, default1.6) sets the maximum zoom level.zoom_min(Number, default0.5) sets the minimum zoom level.zoom_value(Number, default0.1) sets the zoom increment.zoom_last_value(Number, default1) stores the previous zoom level.draggable_inputs(Boolean, defaulttrue) lets input controls start node dragging.useuuid(Boolean, defaultfalse) assigns UUIDs to newly created node IDs.
editor.reroute = true; editor.zoom_min = 0.5; editor.zoom_max = 1.6; editor.zoom_value = 0.1; editor.useuuid = true; editor.start();
API Methods:
Editor, Nodes & Data
// Start rendering and attach editor interactions.
editor.start();
// Register reusable DOM or Vue node content. props and options apply to Vue nodes.
editor.registerNode(name, html, props, options);
// Add a node and return its new node ID.
const nodeId = editor.addNode(name, inputs, outputs, posx, posy, className, data, html, typeNode);
// Get node data by its raw ID, such as 5.
editor.getNodeFromId(5);
// Get an array of IDs for nodes with the same name.
editor.getNodesFromName('profile');
// Remove a node. This method expects the DOM-style ID.
editor.removeNodeId('node-5');
// Replace the data object stored by a node.
editor.updateNodeDataFromId(5, { title: 'Updated' });
// Add or remove node inputs and outputs.
editor.addNodeInput(5);
editor.addNodeOutput(5);
editor.removeNodeInput(5, 'input_2');
editor.removeNodeOutput(5, 'output_2');Connections
// Create a connection between two nodes.
editor.addConnection(5, 8, 'output_1', 'input_1');
// Remove one connection. Returns true when a matching connection exists.
editor.removeSingleConnection(5, 8, 'output_1', 'input_1');
// Recalculate the rendered connection positions for a node.
editor.updateConnectionNodes('node-5');
// Remove every connection attached to a node.
editor.removeConnectionNodeId('node-5');Modules
// Return the module that contains a node.
editor.getModuleFromNodeId(5);
// Create, open, or remove a module.
editor.addModule('Review');
editor.changeModule('Review');
editor.removeModule('Review');
// Clear the active module.
editor.clearModuleSelected();
// Clear all flow data and recreate an empty Home data object.
editor.clear();Zoom
// Increase, decrease, or reset the editor zoom. editor.zoom_in(); editor.zoom_out(); editor.zoom_reset();
Import & Export
// Export the complete Drawflow data object. const savedFlow = editor.export(); // Replace the current editor data and render the imported flow. editor.import(savedFlow);
Events:
editor.on('nodeCreated', function(id) {
console.log(id);
});
editor.on('nodeRemoved', function(id) {
console.log(id);
});
editor.on('nodeDataChanged', function(id) {
console.log(id);
});
editor.on('nodeSelected', function(id) {
console.log(id);
});
editor.on('nodeUnselected', function(value) {
console.log(value);
});
editor.on('nodeMoved', function(id) {
console.log(id);
});
editor.on('connectionStart', function(connection) {
console.log(connection.output_id, connection.output_class);
});
editor.on('connectionCancel', function(value) {
console.log(value);
});
editor.on('connectionCreated', function(connection) {
console.log(connection.output_id, connection.input_id, connection.output_class, connection.input_class);
});
editor.on('connectionRemoved', function(connection) {
console.log(connection.output_id, connection.input_id, connection.output_class, connection.input_class);
});
editor.on('connectionSelected', function(connection) {
console.log(connection);
});
editor.on('connectionUnselected', function(value) {
console.log(value);
});
editor.on('addReroute', function(id) {
console.log(id);
});
editor.on('removeReroute', function(id) {
console.log(id);
});
editor.on('rerouteMoved', function(id) {
console.log(id);
});
editor.on('moduleCreated', function(name) {
console.log(name);
});
editor.on('moduleChanged', function(name) {
console.log(name);
});
editor.on('moduleRemoved', function(name) {
console.log(name);
});
editor.on('click', function(event) {
console.log(event);
});
editor.on('clickEnd', function(event) {
console.log(event);
});
editor.on('contextmenu', function(event) {
console.log(event);
});
editor.on('mouseMove', function(position) {
console.log(position.x, position.y);
});
editor.on('mouseUp', function(event) {
console.log(event);
});
editor.on('keydown', function(event) {
console.log(event);
});
editor.on('zoom', function(zoom_level) {
console.log(zoom_level);
});
editor.on('translate', function(position) {
console.log(position.x, position.y);
});
editor.on('import', function(value) {
console.log(value);
});
function handleExport(data) {
console.log(data);
}
editor.on('export', handleExport);
editor.removeListener('export', handleExport);Alternatives & Related Resources:
- Create Interactive Flowchart With JavaScript And Canvas – diagramflowjs
- Draw SVG Paths Between Two Elements – leader-line
Changelog:
v0.0.60 (09/03/2024)
- Bugfix
v0.0.59 (06/30/2022)
- Bugfix
v0.0.58 (05/04/2022)
- [Bug] Right click when creating a connection
- [Breaking] Input inside nodes are not editable
v0.0.57 (05/03/2022)
- Fix issue where Safari attempts to select text during drag operations
v0.0.56 (04/28/2022)
- Fix chrome zoom
- Fix reroute points add and remove
v0.0.55 (01/04/2022)
- Fix remove listeners
v0.0.54 (11/29/2021)
- Multiples classname nodes
v0.0.53 (11/11/2021)
- Fix Path disapear on chrome zoom
v0.0.52 (10/27/2021)
- Add new event mouseUp
v0.0.51 (10/20/2021)
- Fix Vue 3 AddNodeImport
v0.0.50 (10/20/2021)
- Add support for Vue 3
v0.0.48 (08/05/2021)
- Fix css zoom
v0.0.47 (08/05/2021)
- Fix Misaligned routes when chrome zoom is not 100%
- Removed unused css
v0.0.46 (08/05/2021)
- Add event nodeDataChanged
v0.0.45 (07/10/2021)
- Add event rerouteMoved
- Fix event importon change module
- Removed old code and comments
v0.0.44 (07/08/2021)
- Fixed the parent instance for Vue
v0.0.43 (07/03/2021)
- Fix container query dom.
v0.0.42 (05/15/2021)
- Fix df-attributes updates
v0.0.41 (05/14/2021)
- Fix df- attribute levels
v0.0.40 (04/05/2021)
- Added ‘view’ editor mode
v0.0.39 (03/19/2021)
- Bugfix
v0.0.38 (02/27/2021)
- Fix: data node null value
v0.0.37 (02/20/2021)
- Use of UUID as node id instead if integer index. Swithable between index and UUID use.
v0.0.35 (02/14/2021)
- add: connectionStart & connectionCancel event
v0.0.34 (01/28/2021)
- add: connectionSelected and connectionUnselected events
v0.0.33 (01/12/2021)
- fix: Drag type select
v0.0.32 (12/08/2020)
- add: Support vue3
v0.0.31 (11/29/2020)
- add: updateNodeDataFromId function
v0.0.30 (11/11/2020)
- fix:zoom on translate canvas
- add:zoom_last_value
v0.0.29 (11/08/2020)
- fix: force_first_input with 0 inputs
v0.0.27 (10/25/2020)
- Added clickEnd event
v0.0.26 (10/16/2020)
- Fix removeNodeOutput and removeNodeInput more than 10 items
v0.0.25 (09/22/2020)
- Added context menu event
v0.0.24 (09/16/2020)
- Fix nodemoved fire event (only on move)
v0.0.23 (08/28/2020)
- Removed console log
v0.0.20 (08/25/2020)
- Fix addConnection reroute
v0.0.20 (08/07/2020)
- Added reroute fix curvatures
v0.0.19 (08/06/2020)
- Added reroute fix curvatures
v0.0.18 (08/02/2020)
- Fix reroute connections back connections
v0.0.17 (08/01/2020)
- Fix removeNodeOutput with reroutes points
v0.0.15 (07/30/2020)
- added removeNodeInput And removeNodeOutput
v0.0.14 (07/28/2020)
- added addNodeOutput and addNodeInput function
v0.0.13 (07/24/2020)
- Add removeSingleConnection
v0.0.11 (07/15/2020)
- Fix Remove connection before node removed
v0.0.10 (07/12/2020)
- Added addConnection API.
v0.0.9 (07/12/2020)
- Update
07/06/2020
- Added ‘import’ event







