Tree Layout
The Tree Layout addon provides tree and cluster layout algorithms using d3-hierarchy. It is ideal for visualizing hierarchical data with a single root node, such as file systems, org charts, or decision trees.
Click the buttons to apply different layout directions:
<div x-data="flowCanvas({
nodes: [
{ id: 'root', position: { x: 0, y: 0 }, data: { label: 'CEO' } },
{ id: 'eng', position: { x: 0, y: 0 }, data: { label: 'Engineering' } },
{ id: 'design', position: { x: 0, y: 0 }, data: { label: 'Design' } },
{ id: 'fe', position: { x: 0, y: 0 }, data: { label: 'Frontend' } },
{ id: 'be', position: { x: 0, y: 0 }, data: { label: 'Backend' } },
{ id: 'ux', position: { x: 0, y: 0 }, data: { label: 'UX' } },
],
edges: [
{ id: 'e1', source: 'root', target: 'eng' },
{ id: 'e2', source: 'root', target: 'design' },
{ id: 'e3', source: 'eng', target: 'fe' },
{ id: 'e4', source: 'eng', target: 'be' },
{ id: 'e5', source: 'design', target: 'ux' },
],
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 250px;"
x-init="
$flow.treeLayout({ direction: 'TB', duration: 0 });
document.getElementById('demo-tree-tb').addEventListener('click', () => $flow.treeLayout({ direction: 'TB', duration: 300 }));
document.getElementById('demo-tree-lr').addEventListener('click', () => $flow.treeLayout({ direction: 'LR', duration: 300 }));
">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node">
<div x-flow-handle:target></div>
<span x-text="node.data.label"></span>
<div x-flow-handle:source></div>
</div>
</template>
</div>
</div>
Installation
Install the required peer dependency:
npm install d3-hierarchy
Then register the plugin:
import AlpineFlowHierarchy from '@getartisanflow/alpineflow/hierarchy'
Alpine.plugin(AlpineFlowHierarchy)
With WireFlow
If you're using WireFlow (AlpineFlow's Livewire integration), the core is loaded from the WireFlow vendor bundle. Addons work seamlessly — they share a global registry with the core, regardless of how each was loaded.
// Core from WireFlow vendor bundle
import AlpineFlow from '../../vendor/getartisanflow/wireflow/dist/alpineflow.bundle.esm.js';
// Addon from npm
import AlpineFlowHierarchy from '@getartisanflow/alpineflow/hierarchy';
document.addEventListener('alpine:init', () => {
window.Alpine.plugin(AlpineFlow);
window.Alpine.plugin(AlpineFlowHierarchy);
});
Usage
Trigger a tree layout from any Alpine expression or action using the $flow magic:
$flow.treeLayout({ layoutType: 'tree', direction: 'TB' })
Options
| Option | Type | Default | Description |
|---|---|---|---|
layoutType |
string |
'tree' |
Layout algorithm: 'tree' (tidy tree) or 'cluster' (dendrogram — all leaves at the same depth) |
direction |
string |
'TB' |
Layout direction: 'TB' (top-bottom) or 'LR' (left-right) |
nodeWidth |
number |
150 |
Horizontal spacing allocated per node |
nodeHeight |
number |
100 |
Vertical spacing allocated per node |
adjustHandles |
boolean |
true |
Automatically set handle positions to match the layout direction |
fitView |
boolean |
true |
Fit the viewport to the laid-out graph after layout completes |
duration |
number |
0 |
Animation duration in milliseconds (0 for instant) |
Tree vs. Cluster
- Tree (
'tree') — a tidy tree layout that minimizes the width of the tree while keeping nodes at their natural depth. - Cluster (
'cluster') — a dendrogram layout that places all leaf nodes at the same depth, useful for comparing terminal nodes.
Toggle between tree and cluster — notice how cluster aligns all leaf nodes:
<div x-data="flowCanvas({
nodes: [
{ id: 'root', position: { x: 0, y: 0 }, data: { label: 'Root' } },
{ id: 'a', position: { x: 0, y: 0 }, data: { label: 'A' } },
{ id: 'b', position: { x: 0, y: 0 }, data: { label: 'B' } },
{ id: 'a1', position: { x: 0, y: 0 }, data: { label: 'A-1' } },
{ id: 'a2', position: { x: 0, y: 0 }, data: { label: 'A-2' } },
{ id: 'b1', position: { x: 0, y: 0 }, data: { label: 'B-1' } },
],
edges: [
{ id: 'e1', source: 'root', target: 'a' },
{ id: 'e2', source: 'root', target: 'b' },
{ id: 'e3', source: 'a', target: 'a1' },
{ id: 'e4', source: 'a', target: 'a2' },
{ id: 'e5', source: 'b', target: 'b1' },
],
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 250px;"
x-init="
$flow.treeLayout({ layoutType: 'tree', direction: 'TB', duration: 0 });
document.getElementById('demo-tree-type').addEventListener('click', () => $flow.treeLayout({ layoutType: 'tree', direction: 'TB', duration: 300 }));
document.getElementById('demo-cluster-type').addEventListener('click', () => $flow.treeLayout({ layoutType: 'cluster', direction: 'TB', duration: 300 }));
">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node">
<div x-flow-handle:target></div>
<span x-text="node.data.label"></span>
<div x-flow-handle:source></div>
</div>
</template>
</div>
</div>