flowinger
v1.1.0
Published
A small, dependency-free React Flow alternative: a node-graph and flowchart renderer for React with React Flow's API, ~20 kB gzipped core, SSR, and free undo/redo, export and smart edges
Maintainers
Readme
Status:
1.1— semantic versioning: a change that breaks what that page covers waits for2.0. It is what QueryLens draws its diagrams with, and its API follows React Flow's — 12's, and 13's names as well; Upgrading says what 2.0, which follows React Flow 13, will change, and what 1.0 did. These docs describemain; what is newer than the latest npm release is listed under Unreleased there.
Why Flowinger
- Small, and only what you use. The core is about 19.9 kB gzipped; drag, connect, selection and the rest are opt-in features that add only their own code, so the whole canvas is about 37 kB. In QueryLens, swapping React Flow for it took the diagram chunk from 60.5 kB to 19.0 kB gzipped.
- No dependencies.
reactandreact-dom(18 or 19) are the only peers. Plain DOM, SVG and CSS. - Your components are the nodes. Anything you can render in a
divis a node; handles are measured where they are really drawn, so an edge follows a handle you moved with CSS. - An API you already know. React Flow's names for components, hooks, props, class names,
data-testids and types — all 210 of its exports, and every one of the 302 members of the props, instance and components you write against does something (the audit counts a name that compiles and does nothing apart, and there are none) — measured from its own type declarations, with a migration guide.<ReactFlow>has React Flow's defaults, so moving from React Flow is changing an import. - The "pro" extras, free. Undo/redo, copy and paste, snap guides, export, smart edges, flowchart shapes
and a collapsible tree, each its own entry point (
flowinger/history…), so they cost the core nothing unless you import them. - Server rendering that works. Renders and hydrates on React 18 and 19, with the first paint already framed.
- Accessible when you ask. Keyboard navigation, focusable nodes and edges, ARIA fields — opt-in, so a page that only shows a diagram does not gain tab stops it did not want.
- Tested against the real thing. More than 1,000 unit tests (React Flow itself is the oracle for geometry and behaviour), mutation-tested guards, a real-browser suite, and CI that installs the packed package.
At a glance: Flowinger and React Flow
| | Flowinger | React Flow (@xyflow/react 12) |
|---|---|---|
| Runtime dependencies | 0 | 3 direct (@xyflow/system, zustand, classcat), plus d3-zoom, d3-drag, d3-selection… through @xyflow/system |
| Stylesheet to import | None (CSS variables) | @xyflow/react/dist/style.css |
| Diagram chunk in a real app (QueryLens) | 19.0 kB gzipped | 60.5 kB gzipped |
| Mount 10,000 nodes (benchmarks) | 0.6 s | 1.2 s |
| Mount 10,000 nodes, culled | 55 ms | 594 ms |
| Pan / zoom 10,000 nodes, no culling | 8.5 / 10.7 ms a frame | 32.6 / 33.0 ms a frame |
| Pan / zoom 10,000 nodes, culled | 3.6 / 2.5 ms a frame | 4.2 / 4.7 ms a frame |
| Drag at 10,000 nodes, culled | 4.0 ms a move | 4.8 ms a move |
| Undo/redo, copy-paste, snap lines, export, smart edges | Free, separate entry points | React Flow Pro examples (paid subscription) |
| API | React Flow's names: 210 of 210 exports, 119 of 119 props | — |
| Ecosystem, examples, answers online | Small and new | Large and mature |
The culled pan is level within the noise (React Flow's read 2.6–4.2 ms across runs); the zoom is about half React Flow's cost, and a culled mount a tenth, because Flowinger's first render is culled too. Where React Flow is still ahead is the last row: its ecosystem.
Install
npm install flowinger # or: pnpm add flowinger · yarn add flowingerReact 18 or 19 is a peer dependency. The package ships ESM, CommonJS and TypeScript declarations for both.
Quick start
import {
Background, Controls, FlowingerCanvas, FlowingerProvider, Handle, Position,
type Edge, type Node, type NodeProps,
} from 'flowinger';
function Step({ data }: NodeProps) {
return (
<div className="step">
<Handle id="in" type="target" position={Position.Top} />
{String(data.label)}
<Handle id="out" type="source" position={Position.Bottom} />
</div>
);
}
// Defined outside the component, so they keep their identity between renders.
const nodeTypes = { step: Step };
const nodes: Node[] = [
{ id: 'a', type: 'step', position: { x: 0, y: 0 }, data: { label: 'Scan' } },
{ id: 'b', type: 'step', position: { x: 0, y: 120 }, data: { label: 'Filter' } },
];
const edges: Edge[] = [
{ id: 'a-b', source: 'a', target: 'b', sourceHandle: 'out', targetHandle: 'in', markerEnd: 'arrowclosed' },
];
export function Diagram() {
return (
// The canvas fills its parent, so the parent needs a height.
<div style={{ position: 'relative', height: 400 }}>
<FlowingerProvider>
<FlowingerCanvas nodes={nodes} edges={edges} nodeTypes={nodeTypes}>
<Background />
</FlowingerCanvas>
<Controls style={{ position: 'absolute', right: 12, bottom: 12 }} />
</FlowingerProvider>
</div>
);
}That draws a diagram you can pan and zoom. On <FlowingerCanvas> nothing is draggable, connectable or
selectable until you say so — a viewer wants none of it — so an editor opts in. (<ReactFlow> has React Flow's
defaults, with those on; {...reactFlowDefaults} gives them to any canvas.)
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
<FlowingerCanvas
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={(connection) => setEdges((current) => addEdge(connection, current))}
nodesDraggable
nodesConnectable
elementsSelectable
/>More in Getting started, or run the examples.
Pay for what you use
FlowingerCanvas (also exported as ReactFlow) has every feature and is what most apps want. An app that only
shows a graph needs far less, so the canvas is a small core plus features, and you import the ones you use:
import { FlowingerCore, connectFeature, dragFeature } from 'flowinger';
<FlowingerCore
nodes={nodes} edges={edges}
features={[dragFeature, connectFeature]}
nodesDraggable nodesConnectable
/>| Build | Gzipped |
|---|---|
| FlowingerCore, no features | about 19.9 kB |
| …plus each feature you name | only its own code |
| FlowingerCanvas, every feature | about 37 kB |
Both are held by budgets in CI, measured on a small consumer's real bundle. A prop for a feature the canvas lacks does nothing — and in development the console says so. See Features.
Documentation
| | |
|---|---|
| Getting started | Install, a first diagram, making it interactive, and the first things that trip people |
| API reference | Canvas props, nodes and edges, handles, sub-flows, the instance, controlled flows |
| Components and hooks | Handle, Background, Controls, MiniMap, NodeToolbar, NodeResizer, useReactFlow… |
| Features | What each feature adds, what needs it, and how to write your own |
| The "pro" modules | Undo/redo, copy and paste, snap guides, export, smart edges, shapes, a collapsible tree |
| Theming | The --flowinger-* CSS variables |
| Server rendering | What renders on the server and what waits for the client |
| Migrating from React Flow | Defaults, types, what is missing and what to do instead |
| React Flow coverage | How much of its API this covers, measured |
| Benchmarks | Mount, pan, zoom and drag against React Flow at 1,000–10,000 nodes, and how to re-run them |
Examples
Each is a complete Vite + React + TypeScript project you can copy out and run, and CI builds each one against the packed library on every pull request. To try one in the browser with nothing installed, open it in StackBlitz: layouts · editor · export · presence · save-load · stepper · swimlanes · tree · Next.js.
Is it for you?
Reach for Flowinger when your app draws a graph — a query plan, a syntax tree, a dependency map, a workflow — and what it costs matters: the bundle, the dependencies, the markup.
Reach for React Flow instead when you need something on the missing list and cannot wait for it, or you want its larger ecosystem of examples, templates and answers. Flowinger is not a drop-in for all of React Flow, and does not try to be: it covers the part of the API that apps like the one above use, and says plainly where it stops.
Neither library lays a graph out for you. Feed x / y from d3-hierarchy,
dagre or elkjs — the
layouts example does all three.
Compatibility
| | |
|---|---|
| React | 18 and 19, both tested in CI |
| Modules | ESM and CommonJS, with TypeScript declarations for each (checked with publint and Are the Types Wrong) |
| Rendering | Client, server and hydration (react-dom/server) |
| Browsers | Current evergreen browsers — pointer events, ResizeObserver. The browser suite runs in Chromium, Firefox and WebKit; two-finger touch pinch is unit-tested but not yet seen on a physical device (roadmap) |
Contributing
Bug reports, a repro of something React Flow does differently, layout recipes and examples are all welcome. Start with CONTRIBUTING.md — it says what fits, what "done" means here, and how to run everything:
pnpm install
pnpm dev # the demo
pnpm test # unit tests
pnpm e2e # the real-browser suite (Playwright)
pnpm check:package # publint + Are the Types Wrong on the packed tarballBy taking part you agree to the Code of Conduct. Security issues go to
SECURITY.md, not a public issue. plan.md is the design record: what was built, what
was measured, and what was tried and dropped.
Acknowledgements
Flowinger's API follows React Flow by xyflow, and was built to match its behaviour — by running it, reading its published types, and using it as a test oracle. A few formulas (the bezier control points and where a label sits on the curve) are adapted from it under its MIT licence and credited in NOTICE.md.
License
MIT © Naveen Boddepalli
