Compute Flows
The compute engine propagates data through your flow graph in topological order. Register compute functions per node type, connect nodes with edges, and the engine routes outputs to inputs through handle port names.
Two number nodes feed into an adder — click "Compute" to propagate:
<div x-data="flowCanvas({
nodes: [
{ id: 'num1', type: 'number', position: { x: 0, y: 0 }, data: { label: 'Number', value: 3 } },
{ id: 'num2', type: 'number', position: { x: 0, y: 100 }, data: { label: 'Number', value: 7 } },
{ id: 'add', type: 'adder', position: { x: 300, y: 50 }, data: { label: 'Add' } },
],
edges: [
{ id: 'e1', source: 'num1', sourceHandle: 'value', target: 'add', targetHandle: 'a' },
{ id: 'e2', source: 'num2', sourceHandle: 'value', target: 'add', targetHandle: 'b' },
],
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 250px;"
x-init="
registerCompute('number', { compute: (inputs, data) => ({ value: data.value ?? 0 }) });
registerCompute('adder', { compute: (inputs) => ({ sum: (inputs.a ?? 0) + (inputs.b ?? 0) }) });
document.getElementById('demo-compute-run').addEventListener('click', () => compute());
">
<div x-flow-viewport>
<template x-for="node in nodes" :key="node.id">
<div x-flow-node="node" style="min-width: 120px;">
<div x-flow-handle:target.left="'a'" x-show="node.type === 'adder'" style="top: 30%;"></div>
<div x-flow-handle:target.left="'b'" x-show="node.type === 'adder'" style="top: 70%;"></div>
<div style="font-weight: 600; font-size: 13px;" x-text="node.data.label"></div>
<div style="font-size: 12px; font-family: monospace; opacity: 0.7;"
x-text="node.data.$outputs ? Object.values(node.data.$outputs).join(', ') : node.data.value ?? ''"></div>
<div x-flow-handle:source.right="'value'" x-show="node.type === 'number'"></div>
<div x-flow-handle:source.right="'sum'" x-show="node.type === 'adder'"></div>
</div>
</template>
</div>
</div>
How it works
- Register compute functions for each node type via
registerCompute() - Connect nodes with edges — handle names route data between ports
- Run
compute()to propagate data in topological order - Read results from
node.data.$outputs(reactive)
The engine sorts nodes topologically (sources first, sinks last) and runs each node's compute function with gathered inputs from upstream edges.
registerCompute
Register a compute function for a node type:
$flow.registerCompute('adder', {
compute(inputs, nodeData) {
return { sum: (inputs.a ?? 0) + (inputs.b ?? 0) };
}
});
| Parameter | Type | Description |
|---|---|---|
inputs |
Record<string, any> |
Values gathered from upstream edges, keyed by target handle name |
nodeData |
Record<string, any> |
The node's data object — use for node-specific configuration |
| returns | Record<string, any> |
Output values keyed by source handle name |
Port routing
Data flows through edges via handle port names. The edge's sourceHandle maps to an output key, and targetHandle maps to an input key:
// Edge routes "value" output from num1 to "a" input on adder
{ source: 'num1', sourceHandle: 'value', target: 'adder', targetHandle: 'a' }
When handles are unnamed (no sourceHandle / targetHandle), the port name defaults to 'default':
// Compute function using default port
registerCompute('passthrough', {
compute: (inputs) => ({ default: inputs.default })
});
See also
- Manual vs Auto -- choosing when computation runs
- Reactive Data -- displaying computed values in node templates
- Configuration --
computeModeoption