@pooarasu/flow-builder
v1.0.2
Published
Reusable production-ready standalone React flow builder for React 18+ apps
Maintainers
Readme
@pooarasu/flow-builder
Reusable, production-ready standalone flow builder package for React 18+ applications.
Features
- Fully reusable
<FlowBuilder /> - Controlled and uncontrolled graph modes
- Strongly typed generic nodes/edges (
TypeScript, noany) - Internal standalone state engine + undo/redo history
- Node operations: add/delete/duplicate/resize/toolbar/update data
- Edge operations: connect/delete/custom type/labels/animated edges
- Canvas tools: minimap/controls/background/fit/zoom/snap/pan/multi-select
- Keyboard shortcuts:
Delete,Ctrl/Cmd+C,Ctrl/Cmd+V,Ctrl/Cmd+Z,Ctrl/Cmd+Y,Ctrl/Cmd+A - JSON import/export + localStorage save/restore
- Auto layout via built-in layout engine
- Readonly mode
- Dark/light theme
- Custom toolbar and custom context menu
- Plugin system
- ESM + CJS builds + declaration files
- Tree-shakable output
Install
npm install @pooarasu/flow-builder react react-domBasic Usage (Uncontrolled)
import { FlowBuilder, type FlowNode } from "@pooarasu/flow-builder";
interface NodeData extends Record<string, unknown> {
label: string;
}
const initialNodes: FlowNode<NodeData>[] = [
{
id: "a",
type: "flowBuilderNode",
position: { x: 100, y: 100 },
data: { label: "Start" }
}
];
export const Screen = () => (
<FlowBuilder<NodeData>
defaultNodes={initialNodes}
defaultEdges={[]}
snapToGrid
persistKey="my-flow"
/>
);Controlled Usage
import { useState } from "react";
import { FlowBuilder, type FlowEdge, type FlowNode } from "@pooarasu/flow-builder";
interface NodeData extends Record<string, unknown> {
label: string;
}
export const ControlledFlow = () => {
const [nodes, setNodes] = useState<FlowNode<NodeData>[]>([
{
id: "1",
type: "flowBuilderNode",
position: { x: 150, y: 120 },
data: { label: "Node 1" }
}
]);
const [edges, setEdges] = useState<FlowEdge[]>([]);
return (
<FlowBuilder<NodeData>
nodes={nodes}
edges={edges}
onNodesChange={(_, nextNodes) => setNodes(nextNodes)}
onEdgesChange={(_, nextEdges) => setEdges(nextEdges)}
onNodeAdd={(node) => setNodes((prev) => [...prev, node])}
onNodeDelete={(deleted) =>
setNodes((prev) => prev.filter((entry) => !deleted.some((d) => d.id === entry.id)))
}
onNodeDuplicate={(_, duplicated) => setNodes((prev) => [...prev, ...duplicated])}
onConnect={(_, edge) => {
if (!edge) return;
setEdges((prev) => [...prev, edge]);
}}
onEdgeDelete={(deleted) =>
setEdges((prev) => prev.filter((edge) => !deleted.some((d) => d.id === edge.id)))
}
/>
);
};Advanced Example
<FlowBuilder
theme="dark"
readonly={false}
snapToGrid
snapGrid={[20, 20]}
persistKey="flow-state"
validateNode={(node) => Boolean(node.data)}
validateConnection={(connection) => connection.source !== connection.target}
renderToolbar={({ api }) => (
<button type="button" onClick={() => api.autoLayout("LR")}>
Horizontal Layout
</button>
)}
renderContextMenu={({ menu, api, close }) => (
<div style={{ position: "absolute", left: menu.x, top: menu.y }}>
<button
type="button"
onClick={() => {
api.addNode({ position: { x: menu.x, y: menu.y } });
close();
}}
>
Add node here
</button>
</div>
)}
/>Plugin Example
import { createFlowPlugin } from "@pooarasu/flow-builder";
const auditPlugin = createFlowPlugin({
id: "audit",
onEvent(event, api) {
if (event.type === "node:add") {
api.updateNodeData(event.node.id, (prev) => ({
...prev,
createdAt: new Date().toISOString()
}));
}
}
});
<FlowBuilder plugins={[auditPlugin]} />;Public API
Components
FlowBuilderFlowProvider
Hooks
useFlow()useFlowHistory()useFlowSelection()
Core Props
<FlowBuilder
nodes={nodes}
edges={edges}
onNodesChange={fn}
onEdgesChange={fn}
onConnect={fn}
onNodeAdd={fn}
onNodeDelete={fn}
onEdgeDelete={fn}
readonly={false}
theme="light"
plugins={[...]}
/>Styling
- Default styles are isolated under
.rfb-*class names. - Import from package root includes styles automatically.
- Optional explicit style import:
import "@pooarasu/flow-builder/styles.css";- Theme override example:
.my-flow-wrapper {
--rfb-primary: #2563eb;
--rfb-node-bg: #ffffff;
}Folder Structure
src/
components/
nodes/
edges/
hooks/
store/
utils/
types/
plugins/
styles/
examples/
basic/
tests/Development
npm install
npm run typecheck
npm run test
npm run buildRun example app:
npm install --prefix examples/basic
npm run example:devBuild Output
- ESM:
dist/index.js - CJS:
dist/index.cjs - Types:
dist/index.d.ts - CSS:
dist/styles.css
Publish To npm
- Update package metadata in
package.json(name,version,author). - Login:
npm login - Validate:
npm run prepublishOnly - Publish:
npm publish --access public
Deployment Notes (Vercel / Netlify / CRA / Vite)
- Keep
reactandreact-domas peer dependencies in published package. - Consumer app should install peers explicitly.
- Library works in:
- Vercel-hosted React apps
- Netlify-hosted React apps
- CRA consumers
- Vite consumers
- For SSR apps, render
FlowBuilderon client only (it uses browser APIs like pointer events and ResizeObserver).
Scripts
npm run dev- local package devnpm run build- bundle librarynpm run test- run testsnpm run typecheck- strict TS checknpm run example:dev- run example app
