Use a mounted graph's layout registry to arrange its nodes, then fit the resulting diagram into the canvas. The same graph model drives the JavaScript, Angular, React, and Vue bindings.
Choose a layout
Use the mounted DiagramInstance as the facade for the live diagram. In JavaScript, call getEngine() and await the engine's layout operation. The call changes the live model and returns a layout result; call renderNow() before fitting when you need the repaint immediately.
Registered names include auto, elk, dagre, layered, tree, grid, circular, radial, force, spectral, and community. Use auto when you want the engine to choose from the graph's shape. Use dagre, layered, or elk for pipelines and DAGs; use tree for hierarchies; use grid, circular, or radial for uniform collections; and use force, community, or spectral for networks. An unknown name throws instead of silently leaving the graph unchanged.
The object form supplies layout options. nodeSpacing and rankSpacing control the gaps used by the shipped layered layouts.
JavaScript
Mount real data with render, run the chosen layout, repaint, and frame all content. The host has an explicit height so the fitted diagram has a visible canvas.
jsimport { render } from '@grafloria/element';
const host = document.querySelector('#diagram');
if (!(host instanceof HTMLElement)) throw new Error('The #diagram element is required');
const spec = {
nodes: [
{ id: 'root', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Root' },
{ id: 'left', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Left' },
{ id: 'right', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Right' },
],
edges: [
{ id: 'root-left', source: 'root', target: 'left' },
{ id: 'root-right', source: 'root', target: 'right' },
],
};
host.style.height = '400px';
const instance = render(spec, host);
async function arrange() {
const result = await instance.getEngine().layout('dagre', {
nodeSpacing: 40, rankSpacing: 80,
});
instance.renderNow();
instance.fitView(40);
console.log(result.bounds);
}
void arrange();
The three nodes start at the same position, then appear as a left-to-right tree. result.bounds is the bounding box of the laid-out graph, while fitView(40) frames all content with 40 world units of padding.
Framework bindings
The declarative layout prop runs the layout when its value changes. It does not re-run when node data changes, so it does not fight a user's drag. Set fitView on the flow to fit the rendered graph.
tsimport { Component } from '@angular/core';
import { DiagramCanvasComponent } from '@grafloria/angular';
import type { EdgeSpec, NodeSpec } from '@grafloria/renderer';
@Component({
standalone: true,
imports: [DiagramCanvasComponent],
template: `
<grafloria-diagram-canvas [(nodes)]="nodes" [(edges)]="edges"
[layout]="layout" [plugins]="true" style="display:block;height:100vh" />
`,
})
export class LayoutDemo {
layout = { name: 'dagre', options: { nodeSpacing: 40, rankSpacing: 80 } };
nodes: NodeSpec[] = [
{ id: 'root', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Root' },
{ id: 'left', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Left' },
{ id: 'right', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Right' },
];
edges: EdgeSpec[] = [
{ id: 'root-left', source: 'root', target: 'left' },
{ id: 'root-right', source: 'root', target: 'right' },
];
}
textimport { component$ } from '@builder.io/qwik'; import { GrafloriaFlow, type EdgeSpec, type NodeSpec } from '@grafloria/qwik'; const layout = { name: 'dagre', options: { nodeSpacing: 40, rankSpacing: 80 } }; const nodes: NodeSpec[] = [ { id: 'root', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Root' }, { id: 'left', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Left' }, { id: 'right', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Right' }, ]; const edges: EdgeSpec[] = [ { id: 'root-left', source: 'root', target: 'left' }, { id: 'root-right', source: 'root', target: 'right' }, ]; export default component$(() => ( <div style={{ height: '100vh' }}> <GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} layout={layout} /> </div> ));
tsximport { GrafloriaFlow } from '@grafloria/react';
import type { EdgeSpec, NodeSpec } from '@grafloria/react';
const layout = { name: 'dagre', options: { nodeSpacing: 40, rankSpacing: 80 } };
const nodes: NodeSpec[] = [
{ id: 'root', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Root' },
{ id: 'left', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Left' },
{ id: 'right', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Right' },
];
const edges: EdgeSpec[] = [
{ id: 'root-left', source: 'root', target: 'left' },
{ id: 'root-right', source: 'root', target: 'right' },
];
export default function LayoutDemo() {
return <div style={{ height: '100vh' }}><GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} layout={layout} fitView /></div>;
}
vue<script setup lang="ts"> import { GrafloriaFlow } from '@grafloria/vue'; import type { EdgeSpec, NodeSpec } from '@grafloria/vue'; const layout = { name: 'dagre', options: { nodeSpacing: 40, rankSpacing: 80 } }; const nodes: NodeSpec[] = [ { id: 'root', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Root' }, { id: 'left', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Left' }, { id: 'right', position: { x: 0, y: 0 }, size: { width: 130, height: 52 }, label: 'Right' }, ]; const edges: EdgeSpec[] = [ { id: 'root-left', source: 'root', target: 'left' }, { id: 'root-right', source: 'root', target: 'right' }, ]; </script> <template> <div style="height:100vh"><GrafloriaFlow :default-nodes="nodes" :default-edges="edges" :layout="layout" fit-view /></div> </template>
Angular's [plugins]="true" adds canvas controls, including Fit. After the layout completes, use that control to frame the tree. In the other bindings, fitView performs the same framing as part of the mounted flow.
For an imperative rerun, capture the instance through the binding's initialization callback and call instance.getEngine().layout(...), followed by instance.fitView(40). Angular exposes the same operation as applyLayout() on its canvas component; its layoutDone output fires after the operation completes.
Options that matter
| Option | Type | Default | What it does |
|---|---|---|---|
layout | string | { name: string; options?: Record<string, unknown> } | — | Selects a registered algorithm and optional settings. |
direction | layout option | — | Sets the flow direction for supported layouts. |
nodeSpacing | layout option | — | Sets spacing between nodes where supported. |
rankSpacing | layout option | — | Sets spacing between ranks where supported. |
fitView | boolean | — | Fits the mounted flow's content into its view. |
Pitfalls
- Give the canvas host a real height. A percentage height resolves to zero when its ancestors have no height, leaving nothing to fit.
- A declarative layout reacts to changes in the layout value, not to node changes. Call the instance's engine for an explicit rerun after editing the graph.
elkloads its heavier implementation on first use. Choose it when its layered and port-aware behavior matters; use a smaller shipped layout for a lightweight arrangement.- Layout changes node positions and invalidates stale edge routes. Repaint the instance before measuring or exporting the result.
See it running
Open the auto-layout demo to switch between algorithms on a graph whose nine nodes begin stacked at the origin. The demo runs a layout, repaints, and fits the view after each switch.
For a fixed top-down hierarchy, see the Dagre tree demo. For incremental placement that preserves the existing mental map, see dynamic layouting.
Was this page helpful?