Logo
Neura UI
Loading…
 Logo

Flow

Native left-to-right workflow canvas — pan, zoom, multi-output nodes, drag-to-connect edges, snap grid, toolbar and minimap. Zero third-party graph libraries.

Basic Usage

Pass nodes and edges . Positions are optional — missing ones are auto-laid out left to right.
blade
Empty flow Add nodes to start building your workflow.
Drag nodes · scroll to zoom · drag background to pan · drag a + / right port onto another node to connect · Delete to remove
<neura::flow :nodes="$nodes" :edges="$edges" height="380px" />

Branching workflow

Multiple outputs via data.outputs . Edges use sourceHandle to pick a port.
javascript
Empty flow Add nodes to start building your workflow.
$nodes = [
    ['id' => 'w2', 'data' => [
        'label' => 'Review',
        'outputs' => [
            ['id' => 'ok', 'label' => 'Approve'],
            ['id' => 'ko', 'label' => 'Reject'],
        ],
    ]],
];
$edges = [
    ['source' => 'w2', 'target' => 'w3', 'sourceHandle' => 'ok', 'animated' => true],
    ['source' => 'w2', 'target' => 'w4', 'sourceHandle' => 'ko'],
];

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

Empty canvas

blade
Empty flow Add nodes to start building your workflow.
<neura::flow :nodes="[]" :edges="[]" height="280px" :showMinimap="false" />

Snap, minimap & edges

Tune interaction and chrome with props:
Blade
<neura::flow
    :nodes="$nodes"
    :edges="$edges"
    height="500px"
    :snapToGrid="true"
    :gridSize="16"
    :showMinimap="true"
    :showToolbar="true"
    edgeType="smoothstep"
    :defaultEdgeAnimated="false"
    backgroundClasses="grid"
    toolbarClasses="bottom left"
    :minZoom="0.2"
    :maxZoom="2.5"
    :zoomOnWheelScroll="true"
    :panOnDrag="true"
    :nodesConnectable="true"
    :fitViewOnInit="true"
/>

Data structure

JavaScript
// Node
[
    'id' => 'unique-id',
    'type' => 'step',
    'position' => ['x' => 120, 'y' => 80], // optional
    'data' => [
        'label' => 'Title',
        'description' => 'Subtitle',
        'icon' => '▶',
        'color' => '#3b82f6',   // accent bar
        'status' => 'running',
        'outputs' => [         // multi-output ports (right)
            ['id' => 'success', 'label' => 'Success'],
            ['id' => 'error', 'label' => 'Error'],
        ],
    ],
]

// Edge
[
    'id' => 'e1',              // optional
    'source' => 'node-a',
    'target' => 'node-b',
    'sourceHandle' => 'success', // default: out
    'targetHandle' => 'in',      // default: in
    'type' => 'bezier',          // or smoothstep
    'animated' => true,
    'sourceOffset' => -12,       // shift attachment up/down (px)
    'targetOffset' => 18,
]

// Self-loop (same node)
['source' => 'node-a', 'target' => 'node-a']
Select an edge → drag an endpoint up/down along the node to reposition (live). Drag onto another node to reconnect. Or call flow.updateEdge(id, { target: '3', targetOffset: 20 }) .

Props

Prop Type Default Description
nodesarray[]Initial nodes
edgesarray[]Connections
heightstring500pxCanvas height
directionstringlrFlow direction (left → right)
edgeTypestringbezierbezier | smoothstep (loops always use an orthogonal U-turn)
defaultEdgeAnimatedbooleanfalseAnimate edges created by drag-connect / addEdge
snapToGridbooleantrueSnap node drag to grid
gridSizenumber16Snap grid size
showMinimapbooleantrueOverview map
showToolbarbooleantrueZoom / fit / delete controls
backgroundClassesstringdotsdots | grid | none
minZoom / maxZoomnumber0.25 / 2.5Zoom limits
zoomOnWheelScrollbooleantrueWheel zooms toward cursor
panOnDragbooleantrueDrag empty canvas to pan
nodesDraggablebooleantrueAllow moving nodes
nodesConnectablebooleantrueAllow drawing edges
fitViewOnInitbooleantrueFit graph on load (alias: autoCenter)

Events

Events bubble from the flow root. Listen with Alpine / Livewire:
Event Detail
flow-initnodes, edges, viewport
flow-changeFull toObject() snapshot
flow-connectNew edge
flow-edge-updateEdge reconnected / repositioned
flow-node-selectSelected node ids
flow-nodes-deletedDeleted ids
Blade
<div @flow-change="console.log($event.detail)">
    <neura::flow :nodes="$nodes" :edges="$edges" />
</div>

API

Alpine component: neuraFlow (alias flowEditor ).
Blade
<div x-data="{ flow: null }">
    <div
        x-data="neuraFlow({ nodes: [], edges: [] })"
        x-init="flow = $data"
        style="height: 420px"
        class="flow"
    ></div>

    <button @click="flow.addNode({ data: { label: 'New step' } })">Add</button>
    <button @click="flow.addEdge({ source: '1', target: '2', animated: true })">Connect (animated)</button>
    <button @click="flow.fitView()">Fit</button>
    <button @click="console.log(flow.toObject())">Export</button>
</div>
Methods: addNode , removeNodes , addEdge , updateEdge , removeEdges , fitView , setViewport , zoomIn , zoomOut , toObject , getNode , selectNodes , deleteSelection .