Skip to content

Flow Component Props & Events

Complete reference for the <flow-component> custom element API.


EventPayloadDescription
action(action: Item, value: unknown, patch?: any)Triggered when a node form action is clicked
runNode(nodeId: string)Triggered when user requests node execution
change(change: FlowChange)Emitted when nodes/edges are added, removed, or repositioned

Triggered when an action button within a node’s configuration form is clicked:

<template>
<flow-component @action="handleAction" />
</template>
<script setup>
function handleAction(action, value, patch) {
// action.value: Action identifier (e.g., "add.connection")
// action.label: Display label
// value: Current field value
// patch: Optional patch data
if (action.value === 'add.connection') {
const newConnection = await openConnectionDialog();
// Update node with new connection options
}
}
</script>

Triggered when the user clicks the “Run” button on a node. Use this to execute the node on your backend:

<template>
<flow-component @run-node="handleRunNode" />
</template>
<script setup>
async function handleRunNode(nodeId) {
const { getFlow, pushingNode, pushNode } = window.$useFlowCore;
// Get current flow state
const flow = getFlow();
const node = flow.nodes.find(n => n.id === nodeId);
try {
// Execute on backend
const result = await api.executeNode(nodeId, {
flow,
nodeData: node.data,
});
// Update flow with execution result
pushNode({
node,
status: 'success',
outputs: [{ data: result }],
edges: result.nextEdges,
});
} catch (error) {
pushNode({
node,
status: 'error',
message: error.message,
});
}
}
</script>

Emitted whenever the flow structure is modified:

<template>
<flow-component @change="handleChange" />
</template>
<script setup>
function handleChange(change) {
// change.id: ID of the affected element
// change.element: 'node' | 'edge'
// change.type: 'add' | 'remove' | 'position'
console.log(`${change.element} ${change.id} was ${change.type}d`);
// Auto-save on changes
if (change.type === 'add' || change.type === 'remove') {
debouncedSave();
}
}
const debouncedSave = useDebounceFn(() => {
const { getFlow } = window.$useFlowCore;
saveFlow(getFlow());
}, 1000);
</script>

interface FlowChange {
id: string;
element: 'node' | 'edge';
type: 'add' | 'remove' | 'position';
}

<script setup lang="ts">
import { onMounted, ref } from 'vue';
const flowId = ref('workflow-1');
onMounted(async () => {
const { configure, setFlow } = window.$useFlowCore;
// Load available actions from API
const apps = await api.getAvailableApps();
configure('ion.action', apps);
// Load existing flow
const savedFlow = await api.getFlow(flowId.value);
if (savedFlow) {
setFlow(savedFlow);
}
});
function handleAction(action: any, value: unknown, patch?: any) {
switch (action.value) {
case 'add.connection':
openConnectionModal();
break;
case 'configure.webhook':
openWebhookConfig(value);
break;
}
}
async function handleRunNode(nodeId: string) {
const { getFlow, pushingNode, pushNode, reset } = window.$useFlowCore;
// Reset previous execution state
reset(nodeId);
const flow = getFlow();
// Show loading state
pushingNode({
node: nodeId,
queue: { edge_id: null, target: nodeId },
});
try {
const result = await api.post('/execute', {
flowId: flowId.value,
nodeId,
flow,
});
pushNode({
node: { id: nodeId, data: result.nodeData },
status: 'success',
outputs: result.outputs,
edges: result.processedEdges,
});
} catch (error) {
pushNode({
node: { id: nodeId },
status: 'error',
message: error.message,
});
}
}
function handleChange(change: { id: string; element: string; type: string }) {
console.log('Flow modified:', change);
// Track unsaved changes
hasUnsavedChanges.value = true;
}
async function saveFlow() {
const { getFlow } = window.$useFlowCore;
const flowData = getFlow();
await api.saveFlow(flowId.value, flowData);
hasUnsavedChanges.value = false;
}
</script>
<template>
<div class="flow-editor">
<header>
<h1>Workflow Editor</h1>
<button @click="saveFlow" :disabled="!hasUnsavedChanges">
Save
</button>
</header>
<flow-component
@action="handleAction"
@run-node="handleRunNode"
@change="handleChange"
style="height: calc(100vh - 60px);"
/>
</div>
</template>