Migrating from React Flow
Flowinger follows React Flow’s API for the part of it that apps like QueryLens use — a viewer of trees and plans, with some editing. It is not a drop-in for all of React Flow, so this is a checklist, not a promise. The compiler will find most of what is missing; this page is for the things it will not: defaults that differ, and behaviour that changes without an error.
Every default below was read from @xyflow/react 12.11.6’s source, not remembered.
1. Swap the package
Section titled “1. Swap the package”pnpm remove @xyflow/reactpnpm add flowingerimport { ReactFlow, Background, Controls, useReactFlow } from '@xyflow/react';import '@xyflow/react/dist/style.css';import { ReactFlow, Background, Controls, useReactFlow } from 'flowinger';- Delete the stylesheet import. Flowinger ships none: the default node,
Controlsand edges are styled inline from--flowinger-*CSS variables (see Theming), so they need no CSS file. - Your own CSS keeps working where it selects on
react-flow__*class names — those are kept — but not where it relied on React Flow’s stylesheet (the look of.react-flow__handle, the--xy-*variables, the default node’s border). Restyle those, or set the--flowinger-*variables. ReactFlowisFlowingerCanvasunder React Flow’s name — with Flowinger’s defaults until 1.0, and React Flow’s from 1.0 (see §2) — andReactFlowProviderisFlowingerProvider.FlowingerCoreis the small build with only the features you name.
2. Set the defaults you were relying on
Section titled “2. Set the defaults you were relying on”This is the one that bites. React Flow turns interaction on; Flowinger leaves it off until you ask. One line
gives a canvas React Flow’s defaults — reactFlowDefaults, React Flow’s value for each prop where Flowinger’s
differs:
import { ReactFlow, reactFlowDefaults } from 'flowinger';
<ReactFlow {...reactFlowDefaults} nodes={nodes} edges={edges} /* … */ />From 1.0, <ReactFlow> applies them itself, so changing the import is the whole swap, and <FlowingerCanvas>
keeps Flowinger’s. Until then a <ReactFlow> that leaves any of them out says which, in development. See
Upgrading to 1.0.
| React Flow | Flowinger | |
|---|---|---|
nodesDraggable |
true |
false |
nodesConnectable |
true |
false |
elementsSelectable |
true |
false |
nodesFocusable |
true |
as elementsSelectable |
minZoom |
0.5 |
0.05 |
nodeDragThreshold |
1 px | 3 px |
paneClickDistance, nodeClickDistance |
1 px, 0 px | none: a press is a click however far it moved |
maxZoom |
2 |
2 |
fitView margin |
padding 0.1 — a ratio |
40 px, unless you pass a padding ratio |
deleteKeyCode |
'Backspace' |
'Backspace' |
keyboard: nodes and edges as tab stops, with a role |
on | off — opt in with elementsSelectable / keyboardNavigation |
reactFlowDefaults also has the three key codes in §4 (multiSelectionKeyCode, zoomActivationKeyCode,
panActivationKeyCode). Or set only what you rely on:
<ReactFlow nodesDraggable nodesConnectable elementsSelectable minZoom={0.5} // only if you want to keep React Flow's limit /* … *//>An app that only shows a graph (a viewer) needs none of it, was probably already setting them to false, and
can render <FlowingerCanvas>, which keeps these defaults in 1.0.
3. Types
Section titled “3. Types”Flowinger exports every one of React Flow’s 211 names (OnNodesChange, OnConnect, ReactFlowProps,
ReactFlowInstance, NodeMouseHandler, DefaultEdgeOptions…), so a type import needs only the package name
changed. Two differences are on purpose:
ReactFlowPropsandReactFlowInstanceare not generic.ReactFlowInstance<MyNode, MyEdge>is an error here rather than a type argument quietly ignored. Drop the arguments;useReactFlow()is not generic either.onConnectEndis given aConnectEndState({ connection, isValid }), not React Flow’sFinalConnectionState(the type is exported, but a handler annotated with it is a compile error).Node<Data, Type>,NodeProps<MyNode>,Edge,NodeChange<MyNode>andEdgeChange<MyEdge>are generic as they are in React Flow.
4. What is not there
Section titled “4. What is not there”Nothing of React Flow’s canvas API: every prop of ReactFlow — and every member of the components, the instance and
the types you write against — is named and does something (React Flow coverage,
measured from React Flow’s own type declarations). What is worth knowing is where the behaviour, or the default,
is not React Flow’s:
| React Flow | Flowinger | |
|---|---|---|
multiSelectionKeyCode |
Meta (macOS) / Control | left out: Shift, Control and ⌘ all add; given: only that key |
zoomActivationKeyCode |
Meta / Control; holding it zooms even with zoomOnScroll off |
left out: Ctrl/⌘ only chooses zoom or pan under panOnScroll, and zoomOnScroll off is no zoom; given: React Flow’s behaviour |
panActivationKeyCode |
'Space' |
none unless given; needs panKeyFeature (in FlowingerCanvas) |
Controls showInteractive |
on | off |
interpolate |
'smooth' |
'linear'; 'smooth' is there when asked |
colorMode |
a class, and --xy-* colours |
a class, and — for light — --flowinger-* colours, for what is inside the canvas |
ariaLabelConfig |
labels and descriptions | the same keys; the English labels flowinger already had stay when there is no config |
elevateNodesOnSelect / connectOnClick / autoPanOnNodeFocus… |
on | on — with the interaction itself off until you ask (nodesDraggable etc.) |
A press on a handle is no longer a connection until the pointer has travelled (connectionDragThreshold, 1 px),
so onConnectStart and onConnectEnd are not called for a click — a click is onClickConnectStart — and
nearby handles are snapped to (connectionRadius, 20 units). If your tests press a handle and look for the line
at once, move the pointer first.
If something you use differs from what you expected, open an issue saying what you were building.
5. Things that look the same and are not
Section titled “5. Things that look the same and are not”-
A controlled
viewportis asked, not told. As in React Flow, giveviewportandonViewportChangeand the view is yours: a gesture or an instance call asks for where it would go and nothing moves untilviewportchanges. If you never apply what it asks, the canvas simply cannot be moved (in development it says so, once). -
A node with no component of your own is React Flow’s
default,inputoroutput:idanddata.label, with handles ontargetPosition/sourcePosition. It also shows itsidin bold above the label, which React Flow’s does not. Adefaultof your own innodeTypesreplaces it for nodes with no type and for types nobody registered, as in React Flow. -
Panel,MiniMap,NodeToolbar,HandleandBaseEdgetake their element’s attributes (id,aria-*,data-*…) but not its event handlers; React Flow lets those through, and a handler there would replace what the canvas does with the press. Put a handler on an element inside, or on a wrapper. -
useStorereads ten fields (panZoom,isReady,nodes,edges,transform,width,height,minZoom,maxZoom,nodesInitialized), not React Flow’s whole internal store, anduseStoreApihasgetStateandsubscribebut nosetState. -
useReactFlow()returns one object for the life of the provider. An effect with it in its dependencies runs once. (In React Flow it is rebuilt when the viewport initialises.)viewportInitializedis on it as a getter, so read it when you need it rather than depending on it. -
The instance’s editing calls report changes.
setNodes,updateNodeDataand friends are applied by youronNodesChange(or bydefaultNodes); on nodes you merely pass in they do nothing. WithFlowingerCorethey neededitingFeature. -
A node with no
width/heightis measured, and until then a declaredinitialWidth/initialHeight— or a 150 × 40 fallback — is used forfitViewandgetNodesBounds. -
Accessibility attributes are opt-in. React Flow’s
role="application",aria-roledescriptionand live region are not added. Turn onelementsSelectableorkeyboardNavigation, and useariaLabel. -
There is no attribution badge.
proOptionsandattributionPositionare accepted and do nothing. -
Pointer behaviour on inert nodes differs, for the better. React Flow sets
pointer-events: noneon a node that is neither draggable nor selectable, which silences hover styles and tooltips in it; Flowinger does not.
6. What stays the same
Section titled “6. What stays the same”- The data:
{ id, position, data, type, … }for nodes and{ id, source, target, sourceHandle, … }for edges,applyNodeChanges,applyEdgeChanges,addEdge,useNodesState,useEdgesState. - The
react-flow__*class names,nodrag/nopan/nowheel, and the viewport element’stransform: translate(Xpx, Ypx) scale(Z). - The
data-testids:rf__wrapper,rf__node-<id>,rf__edge-<id>,rf__controls,rf__minimapandrf__background. - Edge geometry: handle anchors, bezier control points and label positions agree with React Flow’s to
within 0.2 units, and
getViewportForBoundsmatches its fit to the pixel.
7. Check it
Section titled “7. Check it”- Run the type checker: a missing prop, hook or field is a compile error, and it is the quickest list of what you need.
- Look at the diagram next to the old build. The things that differ without an error are the
fitViewmargin, the default node’s appearance, and anything interactive you did not set (§2). - Run your end-to-end tests. Selectors on
react-flow__*classes andrf__*test ids carry over; ones on React Flow’sroleoraria-*attributes will not.