Skip to content

Advanced & integrations

Every demo here runs live in your browser — drag, zoom and use the controls. The last section covers capabilities that need an extra dependency, with pointers rather than embedded demos.

A collaborative diagram editor

A complete Figma / Excalidraw-style whiteboard built on Nodus: rectangles, ellipses, diamonds, sticky notes and text labels; drag-to-connect arrows; freehand ink; marquee select and drag-to-move; undo/redo; and PNG / JSON export. Every shape’s look is derived from its data, so the whole board serialises to JSON in a few lines — which also makes real-time collaboration almost free: the demo syncs over a BroadcastChannel, so open it in two browser tabs and edit together, with live cursors and presence. No server, no dependencies.

// A shape is just a node; its visuals come from data, set once on create.
nodus.addNode({
attributes: { x, y, shape: 'diamond', color, text: { content, position: 'center', maxLineLength: 16 } },
data: { kind: 'shape', shape: 'diamond', fill: color, label: content },
});
// Drag from one shape to another to connect them — built in:
nodus.tools.connectNodes.enable({ onEdgeCreated: (edge) => edge.setAttributes({ shape: { head: 'arrow' } }) });
// Drop a shape exactly where the pointer is, in graph space:
nodus.events.on('mousedown', (e) => {
const { x, y } = nodus.view.screenToGraphCoordinates({ x: e.x, y: e.y });
nodus.addNode({ attributes: { x, y /* … */ } });
});
// Multiplayer with zero backend — broadcast the serialised board between tabs:
const room = new BroadcastChannel('diagram');
room.postMessage({ doc: serialiseBoard() }); // on every committed edit
room.onmessage = (e) => applyRemoteDoc(e.data.doc); // newest revision wins

Pick a tool, then draw: shapes drop where you click (or drag to size) · Connector links two shapes with an arrow · Sticky / Text drop a label you type inline (double-click any shape to relabel) · Pen sketches freehand · Eraser removes whatever you touch. Select with a click, Shift-click or a marquee, recolour from the palette, undo/redo (Ctrl/⌘ Z), and export to PNG or JSON. Then open the demo in a second tab to draw together live.

Diagram editor — draw, connect & collaborate live Open in new tab ↗

Custom canvas layers

Draw your own content on a Canvas 2D layer beneath (or above) the graph with nodus.layers.addCanvasLayer(drawFn, opts, level) — backgrounds, grids, annotations, heatmaps.

nodus.layers.addCanvasLayer((ctx) => {
ctx.strokeStyle = '#eef2f7';
for (let x = 0; x < ctx.canvas.width; x += 40) {
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, ctx.canvas.height); ctx.stroke();
}
}, {}, 0); // draw fn first; a low level draws underneath the graph
A custom grid layer behind the graph Open in new tab ↗

DOM overlays

nodus.layers.addOverlay({ element, position }) pins your own DOM element to a graph coordinate. The overlay tracks the node as you pan and zoom — handy for rich HTML annotations, callouts or live widgets.

const el = document.createElement('div');
el.textContent = 'Entry point';
const p = node.getPosition();
nodus.layers.addOverlay({ element: el, position: { x: p.x, y: p.y } });
DOM overlays Open in new tab ↗

Exporting

Export the graph as data or an image. nodus.export.* covers json, svg, csv, gexf, graphml, png, jpg (and xlsx with the optional dependency), and nodus.view.getImageData() gives a raster snapshot.

const json = await nodus.export.json(); // graph data
const svg = await nodus.export.svg(); // vector markup
const png = nodus.view.getImageData(); // raster snapshot
Download JSON, SVG and PNG Open in new tab ↗

Excel (SheetJS)

Build an .xlsx workbook from the graph with SheetJS — one sheet of nodes, one of edges.

const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(nodeRows), 'Nodes');
XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(edgeRows), 'Edges');
XLSX.writeFile(wb, 'graph.xlsx');
Export to Excel Open in new tab ↗

Geographic positioning

Give each node a data.latitude / data.longitude and Nodus’s geo mode overlays the graph on a live Leaflet slippy map — pan, zoom and the nodes stay pinned to their real-world coordinates. Install Leaflet (npm install leaflet), make it available as window.L, then:

await nodus.geo.enable({
tiles: { url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' },
attribution: '© OpenStreetMap contributors',
});
nodus.geo.setView(centroidLat, centroidLng, 2); // centre the map
A graph on a live world map Open in new tab ↗

The demo uses the Canvas renderer so the map tiles show through. Without Leaflet, project the coordinates onto plain x/y for an equirectangular layout instead:

const project = (lat, lng) => ({ x: lng * 7, y: -lat * 7 }); // equirectangular
node.attributes = { x: project(lat, lng).x, y: project(lat, lng).y };

Interactive geo workbench

Geo mode is fully interactive. The 'click' event gives you the pointer in screen space and the element under it; combine it with the map’s coordinate conversion to add, connect and delete points, or draw polygon regions and select the points inside them.

const map = nodus.geo.getMap(); // the Leaflet handle
nodus.events.on('click', (e) => { // { x, y, target, button }
const { lat, lng } = map.containerPointToLatLng(L.point(e.x, e.y));
nodus.addNode({ id, attributes: { radius: 7 }, data: { latitude: lat, longitude: lng } });
});
// regions are plain Leaflet polygons; test membership with point-in-polygon
L.polygon(corners).addTo(map);

Pick a tool, then: Add point drops a node where you click · Connect links two points into a route · Draw region places corners (click the first point, or Finish / Enter, to close) and highlights the points inside · Remove deletes a point and its routes. Pan and zoom — every point stays pinned to its real-world coordinate.

Add points, draw regions, connect & select Open in new tab ↗

Timeline

Scrub a graph over time with the visibility API — a range slider that reveals nodes by a data.year. No plugin required.

function showUpTo(year) {
nodus.getNodes('all').forEach((n) => n.setVisible(n.getData('year') <= year));
}
Timeline scrubber Open in new tab ↗

Performance: large graphs

The Rust/WebAssembly core and GPU renderer are built for scale. This demo builds 1,500 nodes by hand, lays them out, and frames them.

1,500 nodes Open in new tab ↗

For the architecture that makes this fast, see Architecture and The Rendering Pipeline.

UI: legend

nodus.tools.legend.enable() renders a legend that summarises your data-driven styles — one entry per category, with its colour and shape. Track the on/off state yourself and call disable() to hide it.

nodus.styles.addNodeRule({
color: (n) => typeStyle[n.getData().type].color,
shape: (n) => typeStyle[n.getData().type].shape,
});
nodus.tools.legend.enable(); // show the legend
// later: nodus.tools.legend.disable();
Legend Open in new tab ↗

React (and other frameworks)

Nodus is framework-agnostic: create the instance in an effect and give it a container ref. The pattern is the same in any framework.

import { useEffect, useRef } from 'react';
import { Nodus } from '@kortexya/nodus';
export function Graph({ data }) {
const ref = useRef(null);
useEffect(() => {
const nodus = new Nodus({ container: ref.current });
nodus.setGraph(data).then(() => nodus.layouts.force({ duration: 0 }))
.then(() => nodus.view.locateGraph());
return () => nodus.destroy(); // clean up on unmount
}, [data]);
return <div ref={ref} style={{ width: '100%', height: '100%' }} />;
}

See also