ELK Layout

The ELK Layout addon provides access to the full suite of layout algorithms from the Eclipse Layout Kernel via elkjs. ELK offers comprehensive layout strategies including layered, stress, tree, radial, and force.

Click to apply different layout directions:

<div x-data="flowCanvas({
    nodes: [
        { id: 'a', position: { x: 0, y: 0 }, data: { label: 'Start' } },
        { id: 'b', position: { x: 50, y: 50 }, data: { label: 'Process' } },
        { id: 'c', position: { x: 100, y: 0 }, data: { label: 'Review' } },
        { id: 'd', position: { x: 150, y: 50 }, data: { label: 'Approve' } },
        { id: 'e', position: { x: 200, y: 100 }, data: { label: 'End' } },
    ],
    edges: [
        { id: 'e1', source: 'a', target: 'b' },
        { id: 'e2', source: 'a', target: 'c' },
        { id: 'e3', source: 'b', target: 'd' },
        { id: 'e4', source: 'c', target: 'd' },
        { id: 'e5', source: 'd', target: 'e' },
    ],
    background: 'dots',
    fitViewOnInit: true,
    controls: false,
    pannable: false,
    zoomable: false,
})" class="flow-container" style="height: 250px;"
   x-init="
       $flow.elkLayout({ direction: 'DOWN', duration: 0 });
       document.getElementById('demo-elk-down').addEventListener('click', () => $flow.elkLayout({ direction: 'DOWN', duration: 300 }));
       document.getElementById('demo-elk-right').addEventListener('click', () => $flow.elkLayout({ direction: 'RIGHT', duration: 300 }));
       document.getElementById('demo-elk-up').addEventListener('click', () => $flow.elkLayout({ direction: 'UP', duration: 300 }));
       document.getElementById('demo-elk-left').addEventListener('click', () => $flow.elkLayout({ direction: 'LEFT', 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 elkjs

Then register the plugin:

import AlpineFlowElk from '@getartisanflow/alpineflow/elk'

Alpine.plugin(AlpineFlowElk)

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 AlpineFlowElk from '@getartisanflow/alpineflow/elk';

document.addEventListener('alpine:init', () => {
    window.Alpine.plugin(AlpineFlow);
    window.Alpine.plugin(AlpineFlowElk);
});

Usage

Trigger an ELK layout from any Alpine expression or action using the $flow magic:

$flow.elkLayout({ algorithm: 'layered', direction: 'DOWN' })

Options

Option Type Default Description
algorithm string 'layered' Layout algorithm (see table below)
direction string 'DOWN' Layout direction: 'DOWN', 'RIGHT', 'UP', 'LEFT'
nodeSpacing number 50 Minimum spacing between nodes
layerSpacing number 50 Minimum spacing between layers (for layered/mrtree algorithms)
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)

Algorithms

Algorithm Description
'layered' Layer-based approach for directed graphs. Produces clean hierarchical layouts with minimal edge crossings.
'stress' Stress-minimization layout. Positions nodes so that graph-theoretic distances match geometric distances.
'mrtree' Tree layout optimized for graphs with a tree structure.
'radial' Places nodes in concentric circles radiating outward from a root node.
'force' Force-directed layout similar to d3-force but with ELK's implementation.

Try different algorithms — each produces a distinct arrangement:

<div x-data="flowCanvas({
    nodes: [
        { id: 'a', position: { x: 0, y: 0 }, data: { label: 'A' } },
        { id: 'b', position: { x: 10, y: 10 }, data: { label: 'B' } },
        { id: 'c', position: { x: 20, y: 20 }, data: { label: 'C' } },
        { id: 'd', position: { x: 30, y: 30 }, data: { label: 'D' } },
        { id: 'e', position: { x: 40, y: 40 }, data: { label: 'E' } },
        { id: 'f', position: { x: 50, y: 50 }, data: { label: 'F' } },
    ],
    edges: [
        { id: 'e1', source: 'a', target: 'b' },
        { id: 'e2', source: 'a', target: 'c' },
        { id: 'e3', source: 'b', target: 'd' },
        { id: 'e4', source: 'c', target: 'e' },
        { id: 'e5', source: 'd', target: 'f' },
        { id: 'e6', source: 'e', target: 'f' },
    ],
    background: 'dots',
    fitViewOnInit: true,
    controls: false,
    pannable: false,
    zoomable: false,
})" class="flow-container" style="height: 250px;"
   x-init="
       $flow.elkLayout({ algorithm: 'layered', duration: 0 });
       document.getElementById('demo-elk-layered').addEventListener('click', () => $flow.elkLayout({ algorithm: 'layered', duration: 300 }));
       document.getElementById('demo-elk-stress').addEventListener('click', () => $flow.elkLayout({ algorithm: 'stress', duration: 300 }));
       document.getElementById('demo-elk-mrtree').addEventListener('click', () => $flow.elkLayout({ algorithm: 'mrtree', duration: 300 }));
       document.getElementById('demo-elk-force').addEventListener('click', () => $flow.elkLayout({ algorithm: 'force', 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>

See Also