Applying Layouts
A layout computes positions for your nodes. Nodus ships a family of layouts on
nodus.layouts.*, each an async method that returns Promise<void>. This guide
shows how to call them, animate or jump to the result, stop a running layout,
and re-run after the graph changes.
For the conceptual differences between the algorithms, see Layouts. For the full signatures, see API: Layouts.
The available layouts
All of these are methods on nodus.layouts:
| Method | Good for |
|---|---|
force(opts?) | General-purpose, organic spread of any graph |
forceLink(opts?) | Force layout that also respects link/edge structure |
hierarchical(opts?) | Directed/DAG-like data with clear levels |
radial(opts?) | Trees radiating from a root |
concentric(opts?) | Rings of nodes around a centre |
grid(opts?) | A regular grid arrangement |
sequential(opts?) | An ordered, sequence-style arrangement |
Each returns a promise that resolves once positions are settled.
Running a layout
Call a layout method and await it. A typical first render runs a layout, then
fits the result to the viewport.
import { Nodus } from '@kortexya/nodus';
const nodus = new Nodus({ container: document.getElementById('graph') });
await nodus.setGraph({ nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }, { id: 'd' }], edges: [ { source: 'a', target: 'b' }, { source: 'a', target: 'c' }, { source: 'c', target: 'd' }, ],});
await nodus.layouts.force();await nodus.view.locateGraph();view.locateGraph() pans and zooms so the whole graph is visible. Running it
after the layout means it frames the final positions. See
Controlling the Camera for more.
Try it live — switch between all the layouts on the same graph and re-run them:
Animate or jump: the duration option
The one option proven across all layouts is duration (milliseconds):
duration: 0— jump straight to the final positions (static, no animation).- a positive value — animate the transition over that many milliseconds.
// Snap into place instantly — good for large graphs or headless prep.await nodus.layouts.force({ duration: 0 });
// Or animate the nodes gliding to their new positions over 800ms.await nodus.layouts.force({ duration: 800 });Other tuning
Beyond duration, each layout accepts options that tune its behaviour — the
strength of repulsion and attraction for force layouts (gravity,
edgeStrength, theta), the spacing and direction of a hierarchical
layout (direction, nodesSeparation, layerSeparation), the grid shape
(rows, cols, sortBy), and more. They all have defaults — pass only what you
need:
await nodus.layouts.hierarchical({ duration: 600, direction: 'LR', // 'TB' (default) | 'BT' | 'LR' | 'RL' layerSeparation: 80,});See API: Layouts for the per-layout option tables.
Try it live — change the direction and spacing of a hierarchical layout:
Try it live — a radial layout radiating from its centralNode:
Stopping a running layout
Force-directed layouts iterate over time. Call nodus.layouts.stop() to halt
the current layout immediately, leaving nodes wherever they are.
// Kick off a long animated layout.nodus.layouts.force({ duration: 5000 });
// Stop it early — e.g. the user dragged a node or clicked away.stopButton.addEventListener('click', () => { nodus.layouts.stop();});Re-running a layout after changes
When you add or remove nodes, the new elements need positions too. Add the data,
then run the layout again. Because adds and removes are async, await them
first so the layout sees the final graph.
// Grow the graph...nodus.addNodes([{ id: 'e' }, { id: 'f' }]);await nodus.addEdges([ { source: 'd', target: 'e' }, { source: 'e', target: 'f' },]);
// ...then re-layout and re-frame.await nodus.layouts.force({ duration: 600 });await nodus.view.locateGraph({ duration: 600 });This is the same pattern whether you’re expanding a neighbourhood on demand, loading paged results, or letting users build a graph interactively.
Switching layouts
You can run a different layout at any time to re-arrange the same graph. Each call recomputes positions from the current state.
async function showAs(kind, opts = {}) { await nodus.layouts[kind]({ duration: 700, ...opts }); await nodus.view.locateGraph({ duration: 700 });}
await showAs('hierarchical');// later... (radial and concentric need a central node)await showAs('radial', { centralNode: 'root' });The standard pattern
Whatever you’re doing, the reliable sequence is:
// 1. Make sure the graph reflects what you want to lay out.await nodus.setGraph(myData);
// 2. Run a layout (duration: 0 to snap, >0 to animate).await nodus.layouts.force({ duration: 0 });
// 3. Fit the result to the viewport.await nodus.view.locateGraph();Next steps
- Style the result with Styling Nodes & Edges.
- Control framing and animation in Controlling the Camera.
- React to structural changes in Handling Events.