Skip to content

Layout Algorithms

Seven built-in layout algorithms. Switch between them with a single property change.

TypeScript
import { create } from '@topokit/renderer-canvas';

const app = create(container, {
  layout: 'force', // or 'combo-force', 'hierarchical', 'circular', 'dagre', 'radial', 'concentric', 'grid'
  nodes: [...],
  edges: [...],
});

// Switch layout at runtime
app.setLayout('hierarchical');

// Switch with options
app.setLayout('force', { strength: -200, distance: 100, iterations: 300 });

Force-Directed

Default layout. Simulates physical forces -- repulsion pushes nodes apart, edges act as springs. Best for general-purpose graphs and social networks.

TypeScript
app.setLayout('force', {
  strength: -150,     // repulsion (negative = repel)
  distance: 80,       // ideal edge length
  iterations: 300,    // simulation steps
  gravity: 0.1,       // pull toward center
  damping: 0.9,       // velocity decay per tick
});
OptionTypeDefaultDescription
strengthnumber-150Repulsion force. More negative = more spread.
distancenumber80Target edge length.
iterationsnumber300Simulation steps.
gravitynumber0.1Pull toward viewport center.
dampingnumber0.9Velocity decay. Lower = faster settling.
autoCollapseCombosForcebooleanfalseTop-level config option (not nested under layoutOptions). When true, bypasses the “user has already collapsed something → suppress auto-collapse” guard so force layouts always honor autoCollapseCombosThreshold.

Combo-Force

Extends force with two extra passes that keep combo (group) members tightly clustered: a per-combo centroid spring that pulls siblings together, and a centroid-to-centroid Coulomb repulsion between combos. Inter-combo repulsion is applied equally to every member, so each combo translates rigidly rather than deforming.

force behavior is bit-identical when combo-force is not selected — the extra passes only run inside this layout.

TypeScript
app.setLayout('combo-force', {
  comboStrength: 0.15,       // per-combo centroid spring
  comboRepelStrength: 0.5,    // inter-combo Coulomb repulsion multiplier
  comboMinMembers: 2,         // combos smaller than this skip both passes
  // All force-layout options (strength, distance, iterations, gravity, damping) also apply
});
OptionTypeDefaultDescription
comboStrengthnumber0.15Spring constant pulling combo siblings toward their centroid.
comboRepelStrengthnumber0.5Multiplier on centroid-to-centroid Coulomb repulsion.
comboMinMembersnumber2Combos with fewer than this many members skip both extra passes.

Combo membership comes from node.parent. Nested combos are not supported in this version — nesting on the combo container itself is ignored.

Hierarchical

Layers from top to bottom (or left to right). Best for org charts, dependency trees, workflows.

TypeScript
app.setLayout('hierarchical', {
  direction: 'TB',       // 'TB' | 'BT' | 'LR' | 'RL'
  levelSeparation: 120,  // vertical gap between layers
  nodeSeparation: 80,   // horizontal gap between siblings
  treeSpacing: 100,     // spacing between disconnected trees
});
OptionTypeDefaultDescription
direction'TB' | 'BT' | 'LR' | 'RL''TB'Layout direction.
levelSeparationnumber120Space between levels.
nodeSeparationnumber80Space between same-level nodes.
treeSpacingnumber100Space between disconnected trees.

Circular

All nodes on a single circle. Best for relationship visualization and connectivity patterns.

TypeScript
app.setLayout('circular', {
  radius: 300,        // circle radius (auto if omitted)
  startAngle: 0,      // starting angle in radians
  orderBy: 'group',    // order nodes by data field
});

Dagre

Sugiyama-style DAG layout. Minimizes edge crossings. Best for data pipelines and state machines.

TypeScript
app.setLayout('dagre', {
  rankDir: 'TB',       // 'TB' | 'BT' | 'LR' | 'RL'
  rankSep: 80,        // separation between ranks
  nodeSep: 60,        // separation within same rank
  edgeSep: 20,        // separation between edges
  align: 'UL',        // alignment: 'UL' | 'UR' | 'DL' | 'DR'
});

Radial

Concentric circles from a root node outward. Best for ego networks and distance-from-root visualization.

TypeScript
app.setLayout('radial', {
  rootId: 'center-node',   // auto-detected if omitted
  levelSeparation: 100,    // distance between rings
  angleSeparation: 0.5,    // minimum angle between siblings
});

Concentric

Multiple rings based on a numeric property (degree, importance, etc.). Higher-value nodes closer to center.

TypeScript
app.setLayout('concentric', {
  sortBy: 'degree',         // property or function
  minRadius: 80,            // innermost ring radius
  levelSeparation: 60,      // distance between rings
  equidistant: true,        // equal ring spacing
});

Grid

Uniform grid pattern. Best for tabular display where topology is secondary.

TypeScript
app.setLayout('grid', {
  columns: 5,              // auto if omitted
  cellWidth: 120,
  cellHeight: 120,
  sortBy: 'data.name',
});

Choosing the Right Layout

Use CaseLayoutWhy
General explorationforceOrganic positioning reveals clusters
Combo / group-heavy graphscombo-forceKeeps grouped nodes spatially together
Org charts / treeshierarchicalClear parent-child relationships
Data pipelinesdagreOptimal layers, minimal crossings
Social networksforce / circularShows connectivity density
Ego / influence mapsradialDistance = hops from root
Importance rankingconcentricCentral = most important
Catalog / registrygridUniform, predictable

Animated Transitions

Nodes animate smoothly between layouts when switching at runtime.

TypeScript
app.setLayout('circular', {}, {
  animate: true,
  duration: 800,
  easing: 'easeInOutCubic',
});

Next Steps