Reactive Data
After compute() runs, each node's data object is updated with $inputs and $outputs properties. These are reactive — Alpine templates that reference them update automatically.
<div x-data="flowCanvas({
nodes: [
{ id: 'a', type: 'const', position: { x: 0, y: 0 }, data: { label: 'Input A', value: 4 } },
{ id: 'b', type: 'const', position: { x: 0, y: 100 }, data: { label: 'Input B', value: 6 } },
{ id: 'c', type: 'multiply', position: { x: 280, y: 50 }, data: { label: 'Multiply' } },
],
edges: [
{ id: 'e1', source: 'a', sourceHandle: 'value', target: 'c', targetHandle: 'a' },
{ id: 'e2', source: 'b', sourceHandle: 'value', target: 'c', targetHandle: 'b' },
],
background: 'dots',
fitViewOnInit: true,
controls: false,
pannable: false,
zoomable: false,
})" class="flow-container" style="height: 250px;"
x-init="
registerCompute('const', { compute: (inputs, data) => ({ value: data.value ?? 0 }) });
registerCompute('multiply', { compute: (inputs) => ({ product: (inputs.a ?? 0) * (inputs.b ?? 0) }) });
document.getElementById('demo-reactive-compute').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 === 'multiply'" style="top: 30%;"></div>
<div x-flow-handle:target.left="'b'" x-show="node.type === 'multiply'" style="top: 70%;"></div>
<div style="font-weight: 600; font-size: 13px;" x-text="node.data.label"></div>
<div style="font-size: 11px; opacity: 0.5;" x-text="node.data.$inputs ? 'in: ' + JSON.stringify(node.data.$inputs) : ''"></div>
<div style="font-size: 12px; font-family: monospace; color: #22c55e;"
x-text="node.data.$outputs ? 'out: ' + JSON.stringify(node.data.$outputs) : ''"></div>
<div x-flow-handle:source.right="'value'" x-show="node.type === 'const'"></div>
<div x-flow-handle:source.right="'product'" x-show="node.type === 'multiply'"></div>
</div>
</template>
</div>
</div>
$inputs and $outputs
After each compute() call, the engine writes two properties to every computed node's data:
| Property | Type | Description |
|---|---|---|
node.data.$inputs |
Record<string, any> |
Input values gathered from upstream edges, keyed by target handle name |
node.data.$outputs |
Record<string, any> |
Output values returned by the compute function, keyed by source handle name |
These are plain objects on the reactive data — any Alpine expression that reads them will update when computation runs.
Using in templates
Display computed values directly in node markup:
<div x-flow-node="node">
<span x-text="node.data.label"></span>
<!-- Show the computed output -->
<span x-text="node.data.$outputs?.result ?? 'pending'"></span>
<!-- Show what inputs were received -->
<span x-text="node.data.$inputs?.a ?? '—'"></span>
</div>
Conditional rendering
Show different content based on whether computation has run:
<template x-if="node.data.$outputs">
<div x-text="'Result: ' + node.data.$outputs.sum"></div>
</template>
<template x-if="!node.data.$outputs">
<div style="opacity: 0.5;">Not computed</div>
</template>
Events
The compute-complete event fires after each compute() call with the full results map:
@compute-complete="console.log($event.detail.results)"
The results detail is a Map<string, Record<string, any>> — node ID to output data for every node that had a registered compute function.
See also
- Overview -- registerCompute and port routing
- Manual vs Auto -- choosing when computation runs