@theclearsky/react-blender-nodes
v0.0.11
Published
A React component library inspired by Blender's node editor interface, providing a flexible and customizable node-based graph editor for web applications.
Maintainers
Readme

Quick Links
- Interactive examples and component playground
- Install and use in your project
- Report bugs and issues
- Request features and discuss ideas
Overview
React Blender Nodes recreates the iconic Blender node editor experience on the web. Built with modern React patterns and TypeScript, it offers a complete solution for creating interactive node-based interfaces with support for custom nodes, connections, and real-time manipulation. Features an intelligent type system with automatic inference, complex data validation, and comprehensive connection validation to ensure your node graphs are always type-safe and error-free.
Quick Start
Installation
npm install @theclearsky/react-blender-nodesBasic Usage
import {
FullGraph,
useFullGraph,
makeStateWithAutoInfer,
makeTypeOfNodeWithAutoInfer,
makeDataTypeWithAutoInfer,
} from 'react-blender-nodes';
import 'react-blender-nodes/style.css';
function MyNodeEditor() {
// Define data types with auto-infer for type safety
const dataTypes = {
stringType: makeDataTypeWithAutoInfer({
name: 'String',
underlyingType: 'string',
color: '#4A90E2',
}),
numberType: makeDataTypeWithAutoInfer({
name: 'Number',
underlyingType: 'number',
color: '#7ED321',
}),
};
// Define node types with auto-infer for type safety
const typeOfNodes = {
inputNode: makeTypeOfNodeWithAutoInfer({
name: 'Input Node',
headerColor: '#C44536',
inputs: [
{ name: 'Text Input', dataType: 'stringType', allowInput: true },
{ name: 'Number Input', dataType: 'numberType', allowInput: true },
],
outputs: [{ name: 'Output', dataType: 'stringType' }],
}),
};
// Create state with auto-infer for complete type safety
const initialState = makeStateWithAutoInfer({
dataTypes,
typeOfNodes,
nodes: [],
edges: [],
});
const { state, dispatch } = useFullGraph(initialState);
return (
<div style={{ height: '600px', width: '100%' }}>
<FullGraph state={state} dispatch={dispatch} />
</div>
);
}Type Safety with Auto-Infer Helpers
The auto-infer helper functions are essential for type safety in React Blender Nodes. They ensure TypeScript can properly validate type references throughout your graph system:
makeDataTypeWithAutoInfer: Validates data type definitionsmakeTypeOfNodeWithAutoInfer: Validates node type definitions and dataType referencesmakeStateWithAutoInfer: Provides complete type inference for the entire state
Why use them?
- ✅ Compile-time validation: Catch errors before runtime
- ✅ IDE support: Better autocomplete and IntelliSense
- ✅ Refactoring safety: TypeScript ensures consistency when renaming types
- ✅ Runtime safety: Prevents invalid type references
Without auto-infer helpers:
// ❌ No type validation - errors only caught at runtime
const dataTypes = {
stringType: { name: 'String', underlyingType: 'string', color: '#4A90E2' },
};With auto-infer helpers:
// ✅ Full type validation - errors caught at compile time
const dataTypes = {
stringType: makeDataTypeWithAutoInfer({
name: 'String',
underlyingType: 'string',
color: '#4A90E2',
}),
};Features
🎨 Blender-Inspired Interface

- Authentic dark theme matching Blender's node editor
- Familiar interactions and visual design
- Smooth animations and transitions
🔧 Customizable Nodes

- Dynamic inputs and outputs with custom shapes
- Collapsible input panels for complex configurations
- Interactive input components (text, number sliders)
- Custom handle shapes (circle, square, diamond, star, etc.)
🎮 Interactive Graph Editor

- Pan, zoom, and select nodes with intuitive controls
- Drag and drop node connections
- Context menu for adding new nodes
- Real-time node manipulation
🧠 Smart Type System & Validation + Advanced Features
https://github.com/user-attachments/assets/72d9384a-e9ca-4223-906a-dc422fb66f49
- Intelligent Type Inference: Automatically infer node types from
connections
- Dynamic type resolution as you build your graph
- Real-time type updates when connections change
- Support for
inferFromConnectiondata types
- Advanced Type Validation: Comprehensive type checking system
- Complex Type Checking: Zod schema validation for complex data structures
- Type Conversion Control: Fine-grained control over allowed type conversions
- Cycle Detection: Prevent infinite loops in your node graphs
- Multiple Data Types: Support for diverse data structures
- Basic types:
string,number,boolean - Complex types: Custom objects with Zod schemas
- Special types:
inferFromConnection,noEquivalent
- Basic types:
- Runtime Safety: Catch type errors before they break your application
- Connection validation with detailed error messages
- Automatic type propagation across connected nodes
- Schema compatibility checking for complex types
- State Management: Integrated reducer for managing graph state
- TypeScript Support: Full type safety with comprehensive definitions
🚀 Node Runner — Execute Your Graphs

Turn your node graphs into executable programs. The built-in runner compiles your graph into an execution plan and runs it — with full debugging support.
- Two execution modes:
- Instant: Runs the entire graph at once, then replay via the timeline
- Step-by-step: Pause after each node, manually advance with step/resume
- Execution timeline: Multi-track timeline visualization showing each node's
execution as a block, grouped by concurrency level
- Scrubber with drag-to-seek and snap-to-step
- Zoom, pan, and auto-fit controls
- Wall-clock and execution-time display modes (strips out pause/debug overhead)
- Auto-scroll follows the current step during live execution
- Step inspector: Click any timeline block to inspect a step's input values, output values, timing, errors, and loop/group context
- Visual node states: Nodes on the canvas highlight in real time as idle, running, completed, skipped, or errored

- Loop support: Define iterative computation with loop-start/stop/end node
triplets — the runner compiles loop bodies into
LoopExecutionBlocks with per-iteration recording and configurable max-iteration limits - Node groups: Compose subgraphs into reusable group nodes — the compiler
recursively resolves group subtrees into
GroupExecutionScopes - Execution recording: Every run produces a full
ExecutionRecordwith per-step timing, input/output snapshots, and loop iteration details — export and import recordings as JSON for sharing and offline analysis

Usage
import { FullGraph, useFullGraph } from 'react-blender-nodes';
import { makeFunctionImplementationsWithAutoInfer } from 'react-blender-nodes';
// Define what each node type does when executed
const functionImplementations = makeFunctionImplementationsWithAutoInfer({
myNodeType: async ({ inputs }) => {
// Process inputs and return outputs
return { outputHandle: inputs.inputHandle * 2 };
},
});
// Pass implementations to FullGraph to enable the runner
<FullGraph
state={state}
dispatch={dispatch}
functionImplementations={functionImplementations}
/>;useNodeRunner Hook
For advanced control over graph execution, use the useNodeRunner hook directly
instead of relying on the built-in runner UI:
import { FullGraph, useFullGraph, useNodeRunner } from 'react-blender-nodes';
function MyExecutableGraph() {
const { state, dispatch } = useFullGraph(initialState);
const {
// State
runnerState, // 'idle' | 'compiling' | 'running' | 'paused' | 'completed' | 'errored'
nodeVisualStates, // Map<nodeId, 'idle' | 'running' | 'completed' | 'errored' | 'skipped'>
executionRecord, // Full execution recording with per-step timing and I/O snapshots
currentStepIndex, // Index of the currently active/viewed step
// Actions
run, // Start execution (mode-aware: instant or step-by-step)
pause, // Pause during step-by-step execution
resume, // Resume paused step-by-step execution
step, // Advance one step (starts a new run if idle)
stop, // Abort the current execution
reset, // Clear all execution state back to idle
replayTo, // Seek to a specific step index in a completed recording
loadRecord, // Load an imported ExecutionRecord (validates against current graph)
// Settings
mode, // Current execution mode: 'instant' | 'stepByStep'
setMode, // Switch execution mode
maxLoopIterations, // Max iterations before a loop is force-stopped
setMaxLoopIterations,
} = useNodeRunner({
state,
functionImplementations,
options: { maxLoopIterations: 100 },
});
return (
<div>
<button onClick={run}>Run</button>
<button onClick={step}>Step</button>
<button onClick={pause}>Pause</button>
<button onClick={resume}>Resume</button>
<button onClick={stop}>Stop</button>
<button onClick={reset}>Reset</button>
<p>Status: {runnerState}</p>
<FullGraph state={state} dispatch={dispatch} />
</div>
);
}Import/Export & Automatic Repair
Graph state and execution recordings can be exported to JSON and re-imported later. On import, the library validates the structure and can automatically repair common issues via opt-in repair strategies.
State Import Repair Strategies
Pass a repair object to importGraphState to enable automatic fixes:
import { importGraphState } from 'react-blender-nodes';
const result = importGraphState(json, {
dataTypes: myDataTypes,
typeOfNodes: myTypeOfNodes,
repair: {
removeOrphanEdges: true, // Remove edges whose source or target node doesn't exist
removeDuplicateNodeIds: true, // Deduplicate nodes with the same ID (keep first)
removeDuplicateEdgeIds: true, // Deduplicate edges with the same ID (keep first)
fillMissingDefaults: true, // Fill missing optional fields (viewport, etc.) with defaults
rehydrateDataTypeObjects: true, // Rebuild handle dataType objects from provided dataTypes
},
});
if (result.success) {
// result.data is the repaired State
// result.warnings contains info about what was repaired
} else {
// result.errors contains fatal validation issues
}Recording Import Repair Strategies
Pass a repair object to importExecutionRecord for recording-specific fixes:
import { importExecutionRecord } from 'react-blender-nodes';
const result = importExecutionRecord(json, {
repair: {
sanitizeNonSerializableValues: true, // Replace non-serializable values with "[non-serializable]"
removeOrphanSteps: true, // Remove steps referencing nodes not present in the record
},
});All repair strategies default to false and must be explicitly enabled.
Usage Examples
Smart Type System with Validation
import { z } from 'zod';
// Define complex data types with Zod schemas
const userSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
const dataTypes = {
stringType: makeDataTypeWithAutoInfer({
name: 'String',
underlyingType: 'string',
color: '#4A90E2',
}),
userType: makeDataTypeWithAutoInfer({
name: 'User',
underlyingType: 'complex',
complexSchema: userSchema,
color: '#7ED321',
}),
inferredType: makeDataTypeWithAutoInfer({
name: 'Inferred',
underlyingType: 'inferFromConnection',
color: '#FF6B6B',
}),
};
// Enable advanced validation features
const initialState = makeStateWithAutoInfer({
dataTypes,
typeOfNodes: {
userInput: makeTypeOfNodeWithAutoInfer({
name: 'User Input',
inputs: [{ name: 'User Data', dataType: 'userType' }],
outputs: [{ name: 'Output', dataType: 'inferredType' }],
}),
stringProcessor: makeTypeOfNodeWithAutoInfer({
name: 'String Processor',
inputs: [{ name: 'Input', dataType: 'inferredType' }],
outputs: [{ name: 'Result', dataType: 'stringType' }],
}),
},
nodes: [],
edges: [],
// Enable smart validation features
enableTypeInference: true,
enableComplexTypeChecking: true,
enableCycleChecking: true,
allowedConversionsBetweenDataTypes: {
userType: { stringType: true }, // Allow user to string conversion
},
});Custom Node with Panels
const customNode = {
id: 'advanced-node',
type: 'configurableNode',
position: { x: 100, y: 100 },
data: {
name: 'Advanced Processor',
headerColor: '#2D5A87',
inputs: [
{
id: 'direct-input',
name: 'Direct Input',
type: 'string',
handleColor: '#00BFFF',
allowInput: true,
},
{
id: 'settings-panel',
name: 'Settings Panel',
inputs: [
{
id: 'threshold',
name: 'Threshold',
type: 'number',
handleColor: '#96CEB4',
allowInput: true,
handleShape: 'diamond',
},
{
id: 'config',
name: 'Configuration',
type: 'string',
handleColor: '#00FFFF',
allowInput: true,
handleShape: 'star',
},
],
},
],
outputs: [
{
id: 'result',
name: 'Result',
type: 'string',
handleColor: '#FECA57',
handleShape: 'hexagon',
},
],
},
};Handle Shapes Showcase
// Available handle shapes
const handleShapes = [
'circle', // Default circular handle
'square', // Square handle
'rectangle', // Tall rectangle
'diamond', // 45° rotated square
'hexagon', // Regular hexagon
'star', // 5-pointed star
'cross', // Plus/cross shape
'list', // Three horizontal bars
'grid', // 2x2 grid of squares
'trapezium', // Trapezoid shape
'zigzag', // Zigzag pattern
'sparkle', // Sparkle effect
'parallelogram', // Parallelogram shape
];Context Menu Integration
// Right-click anywhere on the graph to open context menu
// Automatically generates "Add Node" menu with all available node types
// Clicking a node type adds it at the cursor position🎨 Styling
The library uses Tailwind CSS for styling and provides a dark theme that matches Blender's aesthetic:
/* Import the default styles */
@import 'react-blender-nodes/style.css';
/* Customize colors using CSS variables */
:root {
--primary-black: #181818;
--primary-dark-gray: #272727;
--primary-gray: #3f3f3f;
--primary-white: #ffffff;
}📚 Documentation
Interactive Documentation
Explore all components with live examples:
npm run storybookVisit http://localhost:6006 to see:
- Component playgrounds
- Interactive controls
- Usage examples
- Handle shape demonstrations
Architecture & Internal Documentation
For contributors and developers building on the library internals, a full
documentation index is available at docs/index.md. It
includes an ASCII architecture diagram, cross-feature dependency maps, and a
"What to Read Based on What You're Building" guide:
| Task | Key Docs |
| ---------------------------- | --------------------------------------------------------------------------- |
| Adding a new data type | dataTypesDoc, handlesDoc, typeInferenceDoc, connectionValidationDoc |
| Adding a new node type | nodesDoc, handlesDoc, configurableNodeDoc, stateManagementDoc |
| Making nodes executable | runnerCompilerDoc, runnerExecutorDoc, runnerHookDoc |
| Building node groups / loops | nodeGroupsDoc, loopsDoc, connectionValidationDoc |
| Modifying graph editor UI | fullGraphDoc, configurableNodeDoc, contextMenuDoc |
| Working with state/reducer | stateManagementDoc, immerDoc, edgesDoc |
See the full index for all 32 feature docs with relative links organized by tier.
Component API
FullGraph
The main graph editor component with full ReactFlow integration.
interface FullGraphProps {
/** The current state of the graph including nodes, edges, and type definitions */
state: State;
/** Dispatch function for updating the graph state */
dispatch: Dispatch;
/** Function implementations for each node type, enables the runner when provided */
functionImplementations?: FunctionImplementations;
/** Called when state is successfully imported. Receives the raw parsed state. */
onStateImported?: (importedState: State) => void;
/** Called when a recording is successfully imported. Receives the parsed ExecutionRecord. */
onRecordingImported?: (record: ExecutionRecord) => void;
/** Called when import validation fails. Receives the error messages. */
onImportError?: (errors: string[]) => void;
/** Controlled execution record. When provided, FullGraph uses this instead of internal state. */
executionRecord?: ExecutionRecord | null;
/** Called whenever the execution record changes (run completes, reset, load, etc.). */
onExecutionRecordChange?: (record: ExecutionRecord | null) => void;
}ConfigurableNode
Customizable node component with dynamic inputs and outputs.
interface ConfigurableNodeProps {
/** Unique identifier for the node (shown when enableDebugMode is true) */
id?: string;
/** Display name of the node */
name?: string;
/** Background color of the node header */
headerColor?: string;
/** Array of inputs and input panels */
inputs?: (ConfigurableNodeInput | ConfigurableNodeInputPanel)[];
/** Array of output sockets */
outputs?: ConfigurableNodeOutput[];
/** Whether the node is currently inside a ReactFlow context */
isCurrentlyInsideReactFlow?: boolean;
/** Props for the node resizer component */
nodeResizerProps?: NodeResizerWithMoreControlsProps;
/** Node type unique id */
nodeTypeUniqueId?: string;
/** Whether to show the node open button (used by node groups) */
showNodeOpenButton?: boolean;
/** Runner visual state for this node (undefined = no runner overlay) */
runnerVisualState?: NodeVisualState;
/** Errors from the runner for this node */
runnerErrors?: ReadonlyArray<GraphError>;
/** Warnings from the runner for this node */
runnerWarnings?: ReadonlyArray<string>;
}🔗 Links
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details on:
- Setting up the development environment
- Code style and conventions
- Submitting pull requests
- Reporting issues
📄 License
MIT License - see LICENSE for details.
🙏 Acknowledgments
- Blender Foundation: For creating the amazing Blender software that inspired this project
- ReactFlow: For providing the foundation for the graph editor functionality
- Shadcn/ui: For the component design system and utilities
Note: This project is not affiliated with Blender Foundation. If you find Blender useful, consider donating to support their work.
Made with ❤️ for the Blender and React communities
