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
Section titled “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 editroom.onmessage = (e) => applyRemoteDoc(e.data.doc); // newest revision winsPick 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.
Custom canvas layers
Section titled “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 graphDOM overlays
Section titled “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 } });Exporting
Section titled “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 dataconst svg = await nodus.export.svg(); // vector markupconst png = nodus.view.getImageData(); // raster snapshotExcel (SheetJS)
Section titled “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');Geographic positioning
Section titled “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 mapThe 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 }); // equirectangularnode.attributes = { x: project(lat, lng).x, y: project(lat, lng).y };Interactive geo workbench
Section titled “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 handlenodus.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-polygonL.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.
Timeline
Section titled “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));}Performance: large graphs
Section titled “Performance: large graphs”The Rust/WebAssembly core and GPU renderer are built for scale. This demo builds a scale-free graph of up to 50,000 nodes by hand, lays it out (GPU-accelerated at scale), and frames it — pan, zoom and spin to read the frame rate.
See the dedicated Performance page for the full story, and Architecture and The Rendering Pipeline for what makes it fast.
UI: legend
Section titled “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();React (and other frameworks)
Section titled “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
Section titled “See also”- Transformations — grouping and collapse/expand.
- Animations — animating styles and the camera.
- API: Generators, parsers & geometry — the export, layers and transformation APIs in full.