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
| Graph | Reach for | Why |
|---|---|---|
| Flowcharts, pipelines, DAGs | elk or layered/dagre |
layered ranking, few crossings; ELK handles ports and nesting best |
| Architecture and system diagrams with zones | architecture |
regions on a grid, boxes sized to their words in rows, straight lines, bends in the gutters |
| Hierarchies, org charts | tree | parent-centered, tidy |
| Networks, clusters | force, community, spectral |
physical spread; community detection groups related nodes |
| Catalogs, galleries | grid, circular, radial | uniform placement |
| Don't want to think | auto | classification + 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
nearsits 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' } }}
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
- Layout demos — every algorithm on a real graph.
- Mermaid & the text format — imported text gets type-aware layout automatically.