Skip to content

Layouts

A layout decides where nodes go. You add nodes and edges with no positions (or arbitrary ones), run a layout, and Nodus computes coordinates that make the structure readable. Layouts are the bridge between “I have a graph” and “I can see it.”

This page explains what each layout is for and how to run one. For task-focused examples see Applying Layouts; for signatures see the Layouts API.

Running a layout

Layouts live under nodus.layouts.*. Each returns a Promise<void> that resolves when the layout has finished (or its animation has played out):

import { Nodus } from '@kortexya/nodus';
const nodus = new Nodus({ container: document.getElementById('graph') });
await nodus.setGraph({
nodes: [{ id: 'a' }, { id: 'b' }, { id: 'c' }],
edges: [
{ source: 'a', target: 'b' },
{ source: 'b', target: 'c' },
],
});
await nodus.layouts.force();
await nodus.view.locateGraph(); // fit the result on screen

The full set:

nodus.layouts.force(opts?);
nodus.layouts.forceLink(opts?);
nodus.layouts.hierarchical(opts?);
nodus.layouts.radial(opts?);
nodus.layouts.concentric(opts?);
nodus.layouts.grid(opts?);
nodus.layouts.sequential(opts?);
nodus.layouts.stop();

Async and cancellable

Layouts are asynchronous because they can be iterative and may run off the main thread. Two consequences:

  • Await them (or chain .then) so you act on final positions, not mid-computation ones.
  • Stop them with nodus.layouts.stop() — useful when the user triggers a new layout before the previous one finishes, or navigates away.
const running = nodus.layouts.force(); // start (don't await yet)
// ... user clicks "cancel" ...
nodus.layouts.stop();
await running; // resolves promptly

The duration option

Every layout accepts a duration (in milliseconds) that controls how the graph reaches its new positions:

  • duration: 0 — jump straight to the final positions. Static and instant; best for first paint or large graphs.
  • duration: 800 (any positive value) — animate the transition, tweening nodes from their current spots to the computed ones.
await nodus.layouts.force({ duration: 0 }); // instant
await nodus.layouts.hierarchical({ duration: 600 }); // animated

Choosing a layout

Different structures want different layouts. Use this as a starting point.

Try it live — run the same graph through all seven layouts and compare:

Compare all seven layouts Open in new tab ↗

Force (force)

A physics simulation: nodes repel, edges pull. It produces organic clusters that reveal communities and overall shape without any hierarchy. The general-purpose default when you don’t know the structure in advance, and the right choice for exploratory views of social, citation or network data.

A force layout that treats edges as springs with a target length. Use it when edge length should mean something — keeping connected nodes a consistent distance apart, or letting weighted edges pull harder. Good for weighted networks where you want spacing to reflect connection strength.

Hierarchical / Sugiyama (hierarchical)

A layered layout for directed acyclic graphs, trees and pipelines. It assigns nodes to layers and orders them to minimise edge crossings — the classic Sugiyama approach. Reach for it for flowcharts, dependency graphs, org charts and anything with a clear flow direction.

Radial (radial)

Places nodes on concentric rings around a root, with distance from the root encoding depth. Ideal for trees or ego-networks where one node is the focus and you want hop-distance to read as radius. Requires a centralNode (the root’s id): nodus.layouts.radial({ centralNode: 'root' }).

Concentric (concentric)

Arranges nodes in rings by a ranking — the highest-ranked nodes in the centre, lower-ranked ones further out. Use it to foreground important nodes (by degree, score, or any metric you map to rank) regardless of connectivity. Like radial, it requires a centralNode: nodus.layouts.concentric({ centralNode: 'root' }).

Grid (grid)

Snaps nodes to a regular grid. Tidy, predictable, and crossing-agnostic — handy for catalogues, small multiples, or any view where uniform spacing beats revealing structure.

Sequential (sequential)

Lays nodes out in a single line / sequence. The right tool for inherently linear data — timelines, ordered steps, or a path you want to read left to right.

Where layouts run

Layouts are compute-heavy, so Nodus runs them in compiled Rust/WASM code, and by default in a Web Worker so the main thread stays responsive while a large graph settles. This is why they’re async. The threading needs no special cross-origin headers. For the full picture see The WebAssembly Backend and Architecture.

Layout factories (advanced)

The everyday API is nodus.layouts.*. For advanced use — registering or pre-configuring a layout instance — Nodus also exports factory functions:

import {
forceFactory,
forceLinkFactory,
gridFactory,
hierarchicalFactory,
radialFactory,
concentricFactory,
sequentialFactory,
} from '@kortexya/nodus';

Most applications never need these — reach for them only when you want to build and hold a configured layout yourself rather than call the instance methods.

Where to go next