Implementation:FlowiseAI Flowise OnConnect
| Attribute | Value |
|---|---|
| Source Repository | FlowiseAI/Flowise |
| Source File | packages/ui/src/views/canvas/index.jsx
|
| Domain | Chatflow_Creation |
| Workflow | Chatflow_Creation |
| Last Updated | 2026-02-12 |
Overview
Concrete ReactFlow event handler for creating validated edges between canvas nodes. The onConnect handler in the Canvas component receives a connection object from ReactFlow, constructs a typed edge, updates the target node's input data with a reference to the source node, and adds the edge to the ReactFlow edges state. Connection validation is handled by a separate isValidConnection callback.
Code Reference
Source Location
- File:
packages/ui/src/views/canvas/index.jsx - Lines: L119-164 (
onConnecthandler) - Lines: L414-449 of
packages/ui/src/utils/genericHelper.js(isValidConnection)
Signature
const onConnect = (params) => {
const newEdge = {
...params,
type: 'buttonedge',
id: `${params.source}-${params.sourceHandle}-${params.target}-${params.targetHandle}`
}
// ... update target node inputs and add edge
}
The connection validation function:
export const isValidConnection = (connection, reactFlowInstance) => {
const sourceHandle = connection.sourceHandle
const targetHandle = connection.targetHandle
const target = connection.target
let sourceTypes = sourceHandle.split('-')[sourceHandle.split('-').length - 1].split('|')
let targetTypes = targetHandle.split('-')[targetHandle.split('-').length - 1].split('|')
if (targetTypes.some((t) => sourceTypes.includes(t))) {
// Check single-input constraint and list anchor rules
// ...
return true
}
return false
}
Import
// onConnect is defined inline in the Canvas component
// isValidConnection is imported from genericHelper:
import { isValidConnection } from '@/utils/genericHelper'
// addEdge is from ReactFlow:
import { addEdge } from 'reactflow'
I/O Contract
Inputs
| Parameter | Type | Required | Description |
|---|---|---|---|
| params | object |
Yes | ReactFlow connection object |
| params.source | string |
Yes | Source node ID |
| params.sourceHandle | string |
Yes | Source handle ID (encodes output name and base classes) |
| params.target | string |
Yes | Target node ID |
| params.targetHandle | string |
Yes | Target handle ID (encodes input name and accepted types) |
Outputs
| Output | Type | Description |
|---|---|---|
| (side effect) | Edge added | A new edge is added to the ReactFlow edges state with type "buttonedge"
|
| (side effect) | Node input updated | The target node's data.inputs[targetInput] is updated with a reference to the source node
|
| (side effect) | Dirty flag set | The canvas is marked as dirty (unsaved changes) |
Implementation Details
Edge Construction
Each new edge receives a deterministic ID composed from the connection parameters:
const newEdge = {
...params,
type: 'buttonedge',
id: `${params.source}-${params.sourceHandle}-${params.target}-${params.targetHandle}`
}
The type: 'buttonedge' tells ReactFlow to render this edge using the custom ButtonEdge component, which includes a delete button on the edge.
Target Node Input Update
The handler extracts the target input name from the target handle ID and updates the node's inputs:
const targetNodeId = params.targetHandle.split('-')[0]
const sourceNodeId = params.sourceHandle.split('-')[0]
const targetInput = params.targetHandle.split('-')[2]
setNodes((nds) =>
nds.map((node) => {
if (node.id === targetNodeId) {
let value
const inputAnchor = node.data.inputAnchors.find((ancr) => ancr.name === targetInput)
const inputParam = node.data.inputParams.find((param) => param.name === targetInput)
if (inputAnchor && inputAnchor.list) {
// List anchor: append to array
const newValues = node.data.inputs[targetInput] || []
if (targetInput === 'tools') {
rearrangeToolsOrdering(newValues, sourceNodeId)
} else {
newValues.push(`{{${sourceNodeId}.data.instance}}`)
}
value = newValues
} else if (inputParam && inputParam.acceptVariable) {
// Variable-accepting param: keep existing value
value = node.data.inputs[targetInput] || ''
} else {
// Single anchor: set to source reference
value = `{{${sourceNodeId}.data.instance}}`
}
node.data = {
...node.data,
inputs: { ...node.data.inputs, [targetInput]: value }
}
}
return node
})
)
Connection Validation (isValidConnection)
The validation function extracts type arrays from handle IDs and checks for type intersection:
// Handle ID format: "nodeId-output-name-TypeA|TypeB"
let sourceTypes = sourceHandle.split('-').pop().split('|').map(s => s.trim())
let targetTypes = targetHandle.split('-').pop().split('|').map(t => t.trim())
// Valid if any source type matches any target type
if (targetTypes.some((t) => sourceTypes.includes(t))) {
// Additional checks: single-input constraint, list anchors
let targetNode = reactFlowInstance.getNode(target)
const targetNodeInputAnchor = targetNode.data.inputAnchors.find((ancr) => ancr.id === targetHandle)
if ((targetNodeInputAnchor && !targetNodeInputAnchor.list &&
!reactFlowInstance.getEdges().find((e) => e.targetHandle === targetHandle))
|| targetNodeInputAnchor?.list) {
return true
}
}
return false
Usage Examples
How onConnect is wired to ReactFlow
import ReactFlow, { addEdge } from 'reactflow'
import { isValidConnection } from '@/utils/genericHelper'
const Canvas = () => {
const [edges, setEdges] = useEdgesState()
const { reactFlowInstance } = useContext(flowContext)
const onConnect = (params) => {
const newEdge = {
...params,
type: 'buttonedge',
id: `${params.source}-${params.sourceHandle}-${params.target}-${params.targetHandle}`
}
// Update target node inputs...
setEdges((eds) => addEdge(newEdge, eds))
}
return (
<ReactFlow
edges={edges}
onConnect={onConnect}
isValidConnection={(connection) =>
isValidConnection(connection, reactFlowInstance)
}
/>
)
}