@agendocerto/flow-sdk
v0.4.0
Published
A clean, type-safe abstraction layer for React Flow with centralized state management, complete event system, and command-based API
Maintainers
Readme
Flow SDK
A clean, type-safe abstraction layer for React Flow with centralized state management, complete event system, and command-based API.
Why Flow SDK?
React Flow is powerful but low-level. Building production applications requires:
- Manual state synchronization between React Flow and your app
- Scattered event handlers across multiple callbacks
- Custom TypeScript types for every operation
- Boilerplate code for common operations
Flow SDK solves these problems with a clean architectural layer that provides:
- ✅ Centralized state management (Zustand + Immer)
- ✅ Complete event system (pub/sub pattern)
- ✅ Command API (imperative operations)
- ✅ Full TypeScript strict typing
- ✅ SOLID principles applied
What Problems It Solves
| Problem | React Flow | Flow SDK Solution | |---------|------------|-------------------| | State Management | Manual sync required | Zustand store with automatic sync | | Event Handling | Scattered callbacks | Centralized EventBus (pub/sub) | | Type Safety | Manual type guards | Strict TypeScript with full inference | | Operations | Boilerplate for each op | Command API for all operations | | Batch Updates | Complex custom logic | Transaction system with rollback | | Testing | Mock React Flow internals | Clean interfaces, easy mocking |
Quick Start
Installation
# Install dependencies
npm install
# Build the library
npm run buildUsing the Compiled Library
After building, you can import the library in your React application:
import { FlowAdapter, useFlowCommands, useFlowEvents } from '@agendocerto/flow-sdk';🎨 Importing Styles (REQUIRED)
Flow SDK provides multiple CSS options to avoid duplication:
// ✅ Option 1: Project WITHOUT Tailwind/shadcn (easiest)
import '@agendocerto/flow-sdk/full.css';
// ✅ Option 2: Project WITH Tailwind/shadcn (recommended, smallest bundle)
import '@agendocerto/flow-sdk/base.css';
// + configure Tailwind (see docs)
// ✅ Option 3: Project WITH Tailwind, WITHOUT shadcn
import '@agendocerto/flow-sdk/base.css';
import '@agendocerto/flow-sdk/variables.css';
// + configure Tailwind (see docs)📚 Important:
- Don't use
full.cssif you already have Tailwind (avoids ~54kb duplication!) - See Complete Styling Guide for detailed scenarios
- Configure
tailwind.config.jswhen usingbase.css(see docs)
Note: See the Quick Start Guide for complete setup instructions including peer dependencies and configuration.
Basic Usage
import { FlowAdapter, useFlowCommands, useFlowEvents } from '@agendocerto/flow-sdk';
function App() {
const commands = useFlowCommands();
// Listen to events
useFlowEvents('node:click', (event) => {
console.log('Node clicked:', event.node.id);
});
// Execute commands
const addNode = () => {
commands.addNode({
type: 'default',
position: { x: 100, y: 100 },
data: { label: 'New Node' }
});
};
return (
<FlowAdapter fitView showBackground showControls>
<button onClick={addNode}>Add Node</button>
</FlowAdapter>
);
}Core Concepts
1. State (Single Source of Truth)
All graph data centralized in one place: nodes, edges, selection, viewport.
import { useFlowState } from '@agendocerto/flow-sdk';
const nodes = useFlowState(state => state.nodes);
const nodeCount = useFlowState(state => state.nodes.length);2. Commands (Imperative API)
Predictable operations with clear semantics.
const commands = useFlowCommands();
// Add
commands.addNode({...});
commands.addEdge({...});
// Update
commands.updateNode(id, {...});
commands.moveNode(id, {x, y});
// Remove
commands.removeNode(id);
commands.removeEdges([id1, id2]);3. Events (Reactive Updates)
Subscribe to any change with a clean pub/sub system.
useFlowEvents('node:dragStop', (event) => {
savePosition(event.node.id, event.node.position);
});
useFlowEvents('connect', (connection) => {
console.log('Connected:', connection);
});Key Features
- 🔒 Type Safe - Full TypeScript strict mode with complete inference
- ⚡ Performance - Optimized selectors, shallow equality, zero unnecessary re-renders
- 🎯 SOLID - Clean architecture, easy to extend and maintain
- 📡 Event-Driven - Complete pub/sub system for all React Flow events
- 🎮 Commands - Imperative API for all graph operations
- 💾 Transactions - Batch operations with commit/rollback support
- 🔌 Dynamic Handles - Multiple inputs/outputs per node with automatic distribution
- 📐 Auto Layout - ELK.js integration for automatic graph layout
- 🎨 shadcn-ui - Modern, accessible UI components included by default
Documentation
Getting Started
- Quick Start Guide - Get productive in 5 minutes
- Core Concepts - Understand the architecture
- Documentation Index - Complete documentation index
API Reference
- Commands API - Complete command reference
- Events API - All events with examples
- Hooks API - React hooks reference
Guides
- Custom Nodes - Build custom node types
- Dynamic Handles - Multiple inputs/outputs
- Auto Layout - Automatic graph layout
- Transactions - Batch operations
Advanced
- Events Pattern - "Events on Completion Only" philosophy
- Zustand v5 Migration - Zustand v5 migration guide
Reference
- shadcn-ui Integration - Using shadcn components
Development & Examples
Running the Example Gallery
Run the interactive example gallery for local development:
npm run devThis starts a development server showcasing all Flow SDK features in the Unified Control Panel example.
Available Scripts
npm run dev # Start development server
npm run build # Build library (ESM + CJS + types)
npm run build:lib # Build library bundles only
npm run build:types # Generate TypeScript declarations
npm run type-check # Type check entire project
npm run type-check:lib # Type check library only
npm run lint # Run ESLint
npm run clean # Remove build artifactsExample Features
The Unified Control Panel demonstrates:
- 🎮 Complete Feature Showcase - All SDK capabilities in one interface
- 📦 Node Management - Add, remove, duplicate, and modify nodes
- 🔗 Edge Management - Create and manage connections
- 🔌 Dynamic Handles - Add/remove handles at runtime
- ✨ Selection System - Box selection and batch operations
- 📐 Auto Layout - Multiple layout algorithms (layered, force, radial)
- 🎨 Custom Node Types - WhatsApp-style components, cards, and more
- ⚡ Transactions - Batch operations with rollback support
See example/UnifiedExample.tsx for the complete implementation.
API Quick Reference
Commands
// Nodes
addNode(node: Omit<Node, 'id'> & { id?: string }): string
removeNode(nodeId: string): void
updateNode(nodeId: string, updates: Partial<Node>): void
moveNode(nodeId: string, position: Position): void
duplicateNode(nodeId: string, offset?: Position): string
// Edges
addEdge(edge: Omit<Edge, 'id'> & { id?: string }): string
removeEdge(edgeId: string): void
updateEdge(edgeId: string, updates: Partial<Edge>): void
reconnectEdge(edgeId, newSource, newTarget, ...): void
// Handles
addInputHandle(nodeId, handleId?, label?, position?, color?): string
addOutputHandle(nodeId, handleId?, label?, position?, color?): string
removeHandle(nodeId: string, handleId: string): void
// Selection
setSelection(nodeIds: string[], edgeIds: string[]): void
addToSelection(nodeIds: string[], edgeIds: string[]): void
clearSelection(): void
// Viewport
fitView(padding?: number): void
centerNode(nodeId: string, zoom?: number): void
zoomTo(zoom: number): void
zoomIn(step?: number): void
zoomOut(step?: number): void
// Transactions
startTransaction(options?: TransactionOptions): void
commit(): CommandResult
rollback(): void
// Layout
applyAutoLayout(algorithm?, direction?, spacing?): Promise<void>
// Utility
clear(): void
reset(nodes?: Node[], edges?: Edge[]): voidEvents
// Node events
useFlowEvents('node:click', handler)
useFlowEvents('node:doubleClick', handler)
useFlowEvents('node:dragStart', handler)
useFlowEvents('node:dragStop', handler)
// Edge events
useFlowEvents('edge:click', handler)
useFlowEvents('edge:mouseEnter', handler)
useFlowEvents('edge:mouseLeave', handler)
// Connection events
useFlowEvents('connect', handler)
useFlowEvents('connect:start', handler)
useFlowEvents('connect:end', handler)
// Selection events
useFlowEvents('selection:change', handler)
useFlowEvents('selection:dragStart', handler)
useFlowEvents('selection:dragStop', handler)
// Viewport events
useFlowEvents('move:start', handler)
useFlowEvents('move:end', handler)
// Lifecycle events
useFlowEvents('init', handler)
useFlowEvents('delete', handler)React Hooks
// Commands
const commands = useFlowCommands()
// State (optimized selectors)
const nodes = useFlowState(s => s.nodes)
const nodes = useFlowNodes() // Shortcut
const edges = useFlowEdges() // Shortcut
const selection = useFlowSelection() // Shortcut
const viewport = useFlowViewport() // Shortcut
const stats = useFlowStats() // Computed stats
// Events
useFlowEvents(eventName, handler, deps?)
useFlowEventsMultiple(handlers, deps?)Architecture Highlights
Clean Separation of Concerns
┌─────────────────────────────────────┐
│ React Components │
│ (UI Layer - FlowAdapter) │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ React Hooks │
│ (useFlowCommands, useFlowEvents) │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Core Interfaces │
│ (IGraphState, IGraphCommands) │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Zustand Store │
│ (State + Commands + Events) │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ React Flow │
│ (Rendering Layer) │
└─────────────────────────────────────┘SOLID Principles
- Single Responsibility: Each module has one clear purpose
- Open/Closed: Extensible through events and interfaces
- Liskov Substitution: Interfaces are fully interchangeable
- Interface Segregation: Focused interfaces (IGraphState, IGraphCommands, IEditorEvents)
- Dependency Inversion: Depends on abstractions, not concrete implementations
Performance
Flow SDK is optimized for zero lag and minimal re-renders:
- ⚡ Events on Completion Only - No events during continuous actions (drag, pan, zoom)
- ⚡ Shallow Equality - Default selector comparison prevents unnecessary re-renders
- ⚡ Optimized Hooks - Memoized selectors (useFlowNodes, useFlowEdges)
- ⚡ Batched Updates - RAF-based batching for position updates
- ⚡ Smart Subscriptions - Subscribe only to needed state slices
Result: 150x better performance compared to naive event handling.
Contributing
This is an internal project. For questions or suggestions, contact the development team.
Changelog
See CHANGELOG.md for version history and breaking changes.
License
Internal use only.
Built with ❤️ using React Flow, Zustand, TypeScript, and shadcn-ui
