Back to Home

Flow

A visual flowchart and workflow editor based on Alpine Flow. Build flow diagrams, DAGs, and node-based UIs with zoom, pan, and drag-and-drop.

Basic Usage

A simple flow with nodes and edges:
Drag to pan, scroll to zoom, use the toolbar at bottom-left.

<neura::flow :nodes="$nodes" :edges="$edges" height="400px" />

Workflow Example

A workflow with multiple nodes and branches (validate, reject, approve):
$nodes = [
    ['id' => 'w1', 'type' => 'step', 'data' => ['label' => 'Submit', 'description' => 'Form sent']],
    ['id' => 'w2', 'type' => 'step', 'data' => ['label' => 'Review', 'description' => 'Check data']],
    ['id' => 'w3', 'type' => 'step', 'data' => ['label' => 'Approved', 'description' => 'Validation OK']],
    ['id' => 'w4', 'type' => 'step', 'data' => ['label' => 'Rejected', 'description' => 'Needs correction']],
    ['id' => 'w5', 'type' => 'step', 'data' => ['label' => 'Complete', 'description' => 'Done']],
];
$edges = [
    ['source' => 'w1', 'target' => 'w2'],
    ['source' => 'w2', 'target' => 'w3'],
    ['source' => 'w2', 'target' => 'w4'],
    ['source' => 'w3', 'target' => 'w5'],
    ['source' => 'w4', 'target' => 'w1'],
];

<neura::flow :nodes="$nodes" :edges="$edges" height="450px" />

Empty Flow

An empty editor with toolbar to add nodes via the UI:
<neura::flow :nodes="[]" :edges="[]" height="350px" />

Custom Node Types

Define your own node types via the nodeDefinitions slot and the x-node directive:
<neura::flow :nodes="$nodes" :edges="$edges">
    <x-slot:nodeDefinitions>
        <div
            x-node="{ type: 'customNode', deletable: true, allowBranching: true }"
            x-data="{ props: { title: '', status: 'pending' } }"
        >
            <div x-ignore>
                <div class="rounded-lg bg-primary-100 dark:bg-primary-900/30 px-4 py-2 border border-primary-200 dark:border-primary-800">
                    <span class="font-medium" x-text="props.title"></span>
                    <span class="text-xs ml-2" x-text="props.status"></span>
                </div>
            </div>
        </div>
    </x-slot:nodeDefinitions>
</neura::flow>


$nodes = [
    ['id' => '1', 'type' => 'customNode', 'data' => ['title' => 'My node', 'status' => 'active']],
];

Zoom and Pan Options

Customize editor behavior:
<neura::flow
    :nodes="$nodes"
    :edges="$edges"
    height="500px"
    :minZoom="0.25"
    :maxZoom="3"
    :panOnScroll="true"
    :panOnDrag="true"
    :zoomOnWheelScroll="false"
    :zoomOnPinch="true"
    :autoCenter="true"
    toolbarClasses="bottom left"
    backgroundClasses="dots"
/>

Data Structure

// Node
[
    'id' => 'unique-id',      // Required: unique identifier
    'type' => 'step',         // Required: node type (must match a registered x-node)
    'data' => [               // Required: persisted data (synced with node props)
        'label' => 'Title',
        'description' => '...',
        // ... custom properties
    ],
]

// Edge (connection between two nodes)
[
    'source' => 'source-node-id',
    'target' => 'target-node-id',
]

Props

Prop Type Default Description
nodes array [] Initial nodes
edges array [] Connections (source, target)
height string 500px Container height
minZoom number 0.5 Minimum zoom level
maxZoom number 2 Maximum zoom level
panOnScroll boolean true Pan on scroll
panOnDrag boolean true Pan on drag
zoomOnWheelScroll boolean false Zoom with wheel (otherwise pan)
zoomOnPinch boolean true Zoom on pinch (mobile)
autoCenter boolean true Center the graph in view on load
toolbarClasses string bottom left Toolbar position
backgroundClasses string dots Background style (dots, grid, etc.)

Alpine Flow Events

Alpine Flow dispatches global events you can listen to:
Event Payload Description
@flow-init.window { data: true } Editor initialized
@flow-new-node-rendered.window { data: 'node-id' } New node rendered
@flow-nodes-deleted.window { data: [id1, id2] } Nodes deleted

<div @flow-init.window="console.log('Flow ready')">
    <neura::flow :nodes="$nodes" :edges="$edges" />
</div>

flowEditor API

To access the editor and its methods (addNode, deleteNode, toObject, etc.), capture a reference via x-init:
<div x-data="{ editor: null }">
    <div
        x-init="editor = $el._x_dataStack?.[0]"
        x-data="flowEditor({ nodes: [], edges: [] })"
        style="height: 500px"
    ></div>
    <button @click="editor.addNode({ id: 'new-1', type: 'step', data: {} })">
        Add node
    </button>
    <button @click="console.log(editor.toObject())">
        Export (nodes, edges, viewport)
    </button>
</div>

Resources

Flow is built on @copyfactory/alpine-flow. See the repo for the full API and advanced examples.
The component requires @copyfactory/alpine-flow. Alpine.js is provided by Livewire. Install dependencies with:
php artisan neura-kit:install-deps