Blog / Use cases
Custom nodes without the props mystery
Two of the most-viewed custom-node questions on Stack Overflow — "Can you pass props to a custom node?" (9.7k views) and "saving additional data to a node after it has been created" (25k views) — are really one question asked from two directions: who owns a node's data? Get that model wrong and every read and write becomes a mystery.
The mental model
A node has exactly one data home: the data dictionary on the node
itself, in the diagram's model. Your component doesn't receive "props you passed" —
it receives the node's data, and updating means writing to the node, not to a
component. Once that clicks, every framework binding is the same pattern wearing
local idiom:
// The spec — identical in all three frameworks:
{ id: 'a', type: 'card', position: { x: 80, y: 90 },
size: { width: 230, height: 110 },
data: { title: 'Build', owner: 'CI' } } // ← the one data home
React — a component per type via nodeTypes, receiving
{ id, data, selected, node } (mark the spec custom: true):
function Card({ data, selected }) {
return <div className={selected ? 'card sel' : 'card'}>{data.title}</div>;
}
<GrafloriaFlow defaultNodes={nodes} defaultEdges={edges} nodeTypes={{ card: Card }} />
Vue — a named slot per type; declaring the slot is the opt-in:
<GrafloriaFlow :default-nodes="nodes" :default-edges="edges">
<template #node-card="{ data }">
<div class="card">{{ data.title }}</div>
</template>
</GrafloriaFlow>
Angular — an ng-template per type, same auto-opt-in:
<grafloria-diagram-canvas [(nodes)]="nodes" [(edges)]="edges">
<ng-template grafloriaNode="card" let-data="data">
<div class="card">{{ data['title'] }}</div>
</ng-template>
</grafloria-diagram-canvas>
And the 25k-view question: writing data later
Because the node owns its data, "saving additional data after creation" is a write to the node — tracked for undo and events like any other model change:
const node = instance.getModel().getNode('a');
node.setData('status', 'passing'); // undoable, observable, serialized
instance.renderNow();
No syncing a parallel store, no cloning the nodes array to smuggle a field in. The
escape hatch is symmetric: your component/template also receives the live
node, so reads that outgrow data have somewhere to go.
Deep guides per framework: React · Vue · Angular · plain JavaScript.