Learn / FAQ & troubleshooting
FAQ & troubleshooting
Every entry here is a wall someone actually hit — most of them us, while verifying the tutorials. Symptoms first, causes second.
The canvas is blank
The container has no resolved height. The canvas fills 100% of its parent — and
100% of zero is an empty page. Give the wrapper a real height
(height: 100vh, a flex row with flex: 1, a sized grid cell).
This is the #1 first-run issue in every framework.
My custom node renders as a plain rectangle
In plain JS and React the spec must carry custom: true — that is what
routes the node through the HTML layer where your renderer/component mounts. Vue slots
(#node-<type>) and Angular templates
(ng-template grafloriaNode="type") set it for you; nodeTypes
and registerNodeType do not.
instance.undo is not a function
Correct — history lives on the engine: await instance.getEngine().undo().
Angular's canvas component mirrors undo()/redo() directly;
nothing else does. Keyboard ⌘Z/Ctrl+Z works everywhere with zero wiring.
I can't see any ports
Ports show on hover by default. Show them permanently with
interaction: { portVisibility: 'always' } at creation (a prop in
React/Vue) or engine.setInteractionConfig({ portVisibility: 'always' })
at runtime.
My connection validator fires on a completely different page
The validator registry is process-global, not per-canvas. Keep the disposer
returned by registerConnectionValidator and call it on unmount (plus
clearConnectionValidators() if you want a clean slate). React StrictMode
double-invokes effects — the cleanup protects you there too.
The layout prop doesn't re-run when my data changes
Deliberate. layout re-runs when the prop value changes, never
when node data changes — otherwise every user drag would be fought by a relayout.
Re-run on demand: instance.getEngine().layout('elk') (then
renderNow()), or applyLayout() in Angular/Vue.
Re-applying edited data keeps stale nodes
setNodes/loadText reconcile: ids that still exist
keep their live objects. When you re-apply externally-edited data for the same ids,
clear first — api.setEdges([]); api.setNodes([]); then apply.
Export throws "the canvas has not rendered yet"
Export needs a painted canvas — call it after the first paint (in practice: after
onInit/ngAfterViewInit plus a frame, or behind a user
action). SVG returns a raw string; PNG/PDF return data: URLs.
ESM, require(), Jest
- Since engine 0.3.x / renderer 0.4.x / element 0.4.x the packages are
pure ESM. Bundlers need no config; Node ≥ 20.19 can even
require()them. - Jest with CJS transforms: allow the packages through
(
transformIgnorePatterns: ['/node_modules/(?!@grafloria)']) or run Jest/Vitest in ESM mode. Test failures like "Unexpected token export" are runner config, not library bugs. - Importing
@grafloria/elementregisters the<grafloria-flow>element as a side effect — safe in Node and workers (it no-ops withoutcustomElements).
Isn't 9 MB unpacked huge?
npm's unpacked-size stat counts uncompressed ESM source plus full TypeScript declarations — none of it ships. What your users download, measured worst-case: engine 228 KB gz, react/vue 334, angular 395, element 451 — with ELK layout in a lazy chunk (432 KB gz) that only downloads if invoked. Every package README carries a two-minute reproduction script.
The element ignores my nodes attribute
Malformed JSON — the element warns
([grafloria-flow] ignoring malformed JSON attribute) and keeps running.
Single-quote the attribute, double-quote the JSON inside, and remember geometry is
"position":{"x":0,"y":0} — never top-level x/y. After mount, prefer
properties: el.nodes = [...].