Learn / Layout

Layout

Hand-placing nodes stops scaling around fifteen of them. Grafloria puts eleven layout algorithms behind one registry and one call — and keeps the heavyweight (ELK) out of your bundle until the moment it is used.

One call

await engine.layout('elk');                                   // opinionated default
await engine.layout('dagre', { direction: 'TB', rankSpacing: 80 });
await engine.layout();                                        // 'auto' — picks for you

Registered names: auto, architecture, elk, dagre, layered, tree, grid, circular, radial, force, spectral, community. An unknown name throws with the list of registered ones — no silent no-op. auto inspects the graph (a tree? a DAG? a hairball?) and dispatches to the right algorithm.

Choosing

GraphReach forWhy
Flowcharts, pipelines, DAGselk or layered/dagre layered ranking, few crossings; ELK handles ports and nesting best
Architecture and system diagrams with zonesarchitecture regions on a grid, boxes sized to their words in rows, straight lines, bends in the gutters
Hierarchies, org chartstreeparent-centered, tidy
Networks, clustersforce, community, spectral physical spread; community detection groups related nodes
Catalogs, galleriesgrid, circular, radialuniform placement
Don't want to thinkautoclassification + dispatch

Architecture — a composition, not a ranking

The diagrams AI tools hand-draw as SVG — a user on the left, "their side" and "our side" as tinted zones, a name over a muted line in every box — are not graphs to rank. Layered, ELK and dagre draw them correctly and badly: zones packed over their captions, lines U-turning, labels on labels. architecture composes instead:

  • Regions on a grid. Children go into columns along the flow; zones joined by a line that names a cross side (from:top) stack, the upper one where it says.
  • Boxes sized to their words, sharing a width down a column and a height along a row; stacked zones share one column grid, so the box over a box lines up.
  • Straight lines. A box that talks to several stacked boxes spans them; every line gets anchors at a height both its boxes cover, several lines between one pair spread out; a line that cannot run straight bends in the gutter between the zones.
  • Room for words. A gap is as wide as the widest label crossing it; a zone keeps a band for its caption; a note with near sits beside what it is about.
render({ nodes, edges, groups, layout: 'architecture' }, el);   // on mount — nodes need no position
await engine.layout('architecture');                            // any time, any diagram
<GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} layout="architecture" />   // React (Vue, Angular: same prop)

// the relations it reads — none of them a coordinate
{ id: 'hp', children: ['page', 'wallets'], direction: 'LR' }         // a zone lays its boxes in a row
{ source: 'api', target: 'wallets', sourceHandle: 'top' }            // the wallets sit ABOVE the API
{ id: 'note', shape: { type: 'text' }, near: { target: 'fake', side: 'right' } }

In Mermaid it is one comment: %%grafloria:layout architecture — see the Mermaid page.

RL and BT run the flow backwards (a container's content mirrors along its own flow); four or more boxes with no line between them wrap into a grid in reading order. Mermaid's architecture-beta (its lines name their sides) and block-beta (an explicit grid, columns N, spans and holes) import straight into this layout.

Live: the same text, architecture vs layered →

Live: Mermaid architecture-beta and block-beta →

The cost model — why ELK doesn't bloat you

ELK is the best layered engine available and also ~1.4 MB minified. Grafloria loads it through a lazy import() — a separate chunk (~432 KB gz) that downloads on the first layout('elk') call and never before. It also runs off-thread in a Worker, so a thousand-node layout doesn't freeze your UI. Everything else (dagre, tree, force, grid…) is small and ships eagerly.

Declarative, in every framework

// React
<GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} layout="elk" />

// Angular
<grafloria-diagram-canvas [(nodes)]="nodes" [(edges)]="edges" [layout]="'elk'" />

// object form everywhere
layout={{ name: 'dagre', options: { direction: 'LR' } }}
The binding re-runs when its value changes — never when node data changes. That is deliberate: a user drag round-trips through the nodes binding, and a re-layout firing on every change would fight the user's hands. Re-run on demand with instance.getEngine().layout(...) (React/Vue) or applyLayout() (Angular).

Incremental layout

After inserting into an already-laid-out graph, a full re-layout scrambles the user's mental map. layoutIncremental moves only the neighborhood:

await engine.layoutIncremental({ changed: ['inserted-node-id'], direction: 'LR', radius: 1 });

Live: incremental layout on insert →

Where next