Solid Flow is a port of React Flow and Svelte Flow for SolidJS.
☣️ Solid Flow is alpha and currently under development. The API intends to follow React/Svelte Flow closely but some things might change for the sake of SolidJS. ☣️
- onlyRenderVisibleElements: render only visible elements.
- Custom MiniMap Nodes: define custom minimap jsx node elements.
- Edge Reconnect Anchors: the ability to re-connect already connected edges.
- Easy to use: Seamless zooming and panning, single- and multi selection of graph elements and keyboard shortcuts are supported out of the box
- Customizable: Different node types (Input, Output, Default, Group) and edge types (Bezier, Straight, Step, SmoothStep) with full support for custom nodes and edges
- Fast rendering: Only nodes that have changed are re-rendered using SolidJS's fine-grained reactivity
- Rich Plugin Ecosystem: Background patterns, Interactive MiniMap, Zoom Controls, Node Toolbar, and Node Resizer components
- Powerful Hooks: Comprehensive set of reactive hooks for nodes, edges, viewport, connections, and data management
- Full Accessibility: Complete keyboard navigation, screen reader support, ARIA labels, and focus management
- Drag & Drop: Built-in dragging for nodes, external drag-and-drop support, and customizable drag handles
- Advanced Features: Node grouping, intersection detection, connection validation, and subflow support
- TypeScript First: Fully typed API with generic type support and IntelliSense integration
The easiest way to get the latest version of Solid Flow is to install it via npm, yarn or pnpm:
npm install @dschz/solid-flow
pnpm install @dschz/solid-flow
yarn install @dschz/solid-flow
bun install @dschz/solid-flowThis is a basic example to get you started. For more advanced examples and full API documentation, explore the playground examples included in this repository.
import{SolidFlow,SolidFlowProvider,Controls,Background,MiniMap,addEdge,typeEdgeConnection,createEdgeStore,createNodeStore,typeViewport,}from"@dschz/solid-flow";import"@dschz/solid-flow/styles";// Required stylesimport{createStore,produce}from"solid-js/store";exportdefaultfunctionPage(){return(<SolidFlowProvider><Flow/></SolidFlowProvider>)}functionFlow(){// Can invoke useSolidFlow due to parent Page + SolidFlowProvider wrapper. Contains all helper APIsconst{..}=useSolidFlow();// Use createNodeStore and createEdgeStore for reactive state managementconst[nodes,setNodes]=createNodeStore([{id: "1",type: "input",data: {label: "Input Node"},position: {x: 250,y: 0},},{id: "2",type: "default",data: {label: "Default Node"},position: {x: 100,y: 100},},{id: "3",type: "output",data: {label: "Output Node"},position: {x: 250,y: 200},},]);const[edges,setEdges]=createEdgeStore([{id: "e1-2",source: "1",target: "2"},{id: "e2-3",source: "2",target: "3"},]);const[viewport,setViewport]=createStore<Viewport>({x: 100,y: 100,zoom: 5,});constupdateViewport=()=>{setViewport("x",(prev)=>prev+10);};constonConnect=(connection: EdgeConnection)=>{/** * Solid Flow updates the node/edge stores internally. The user-land edge store will have the connection inserted by the time onConnect fires so we can just go ahead and update the state of it */setEdges((edge)=>edge.id===connection.id,produce((edge)=>{edge.animated=true;}),);};return(<SolidFlownodes={nodes}edges={edges}onConnect={onConnect}fitView><Controls/><MiniMap/><Panelposition="top-left"><buttononClick={updateViewport}>Update viewport</button></Panel><Backgroundvariant="dots"/></SolidFlow>);}- InputNode - Nodes with source handles only (starting points)
- OutputNode - Nodes with target handles only (ending points)
- DefaultNode - Standard nodes with both source and target handles
- GroupNode - Container nodes for organizing other nodes
- BezierEdge - Smooth curved connections (default)
- StraightEdge - Direct straight line connections
- StepEdge - Right-angle step connections
- SmoothStepEdge - Rounded step connections
- Background - Customizable canvas backgrounds (dots, lines, cross patterns)
- Controls - Zoom in/out, fit view, lock/unlock interactions
- MiniMap - Interactive overview with viewport indicator
- NodeToolbar - Context-sensitive toolbars for nodes
- NodeResizer - Real-time node resizing with handles
// Main flow instance with full APIconstsolidFlow=useSolidFlow();// Reactive access to nodes and edgesconstnodes=useNodes();constedges=useEdges();// Viewport control and monitoringconstviewport=useViewport();// Connection state during drag operationsconstconnection=useConnection();// Reactive access to node dataconstnodeData=useNodesData(["node-1","node-2"]);// Node connection informationconstconnections=useNodeConnections("node-1");// Create reactive stores (replaces signals)const[nodes,setNodes]=createNodeStore(initialNodes);const[edges,setEdges]=createEdgeStore(initialEdges);// Update stores with SolidJS patternsimport{produce}from"solid-js/store";setNodes((node)=>node.id==="1",produce((node)=>{node.position.x+=20;}),);// Add new connectionssetEdges(addEdge(connection,edges));// Coordinate transformations (via useSolidFlow)const{ screenToFlowPosition, flowToScreenPosition }=useSolidFlow();// Node/edge utilitiesgetNodesBounds(nodes);getIntersectingNodes(node,nodes);Create fully customized components with multiple handles:
import{Handle,typeNodeProps}from"@dschz/solid-flow";// Type-safe custom node componentfunctionCustomNode(props: NodeProps<{label: string},"custom">){return(<divclass="custom-node"style={{padding: "10px",background: "white"}}><Handletype="target"position="top"/><div>{props.data.label}</div><Handletype="source"position="bottom"id="output-a"/><Handletype="source"position="bottom"id="output-b"style={{left: "80%"}}/></div>);}// Create type-safe node typesconstnodeTypes={custom: CustomNode,}satisfiesNodeTypes;// Use with typed storeconst[nodes]=createNodeStore<typeofnodeTypes>([...]);<SolidFlownodeTypes={nodeTypes}nodes={nodes}.../>import{typeConnection}from"@dschz/solid-flow";constisValidConnection=(connection: Connection)=>{// Custom validation logicreturnconnection.source!==connection.target;};constonConnect=(connection: Connection)=>{console.log("New connection:",connection);setEdges(addEdge(connection,edges));};<SolidFlowisValidConnection={isValidConnection}onConnect={onConnect}.../><SolidFlowonNodeClick={(event,node)=>console.log("Node clicked:",node)}onNodeDrag={(event,node)=>console.log("Node dragged:",node)}onEdgeClick={(event,edge)=>console.log("Edge clicked:",edge)}onPaneClick={(event)=>console.log("Pane clicked")}onSelectionChange={(params)=>console.log("Selection changed:",params)}/>Solid Flow includes comprehensive accessibility features:
- Full keyboard navigation support
- Screen reader compatibility with ARIA labels
- Focus management and visual indicators
- High contrast and color mode support
- Customizable keyboard shortcuts
- Reactive Updates: Only re-renders components when their specific data changes
- Viewport Optimization: Option to render only visible elements (coming soon)
- Memory Efficient: Optimized data structures for large graphs
- Stress Tested: Handles hundreds of nodes smoothly
The repository includes a comprehensive playground with 25+ examples:
- Basic Usage - Simple flows and interactions
- Custom Nodes - Creating specialized node types
- Edge Types - Different connection styles
- Drag & Drop - External elements and node creation
- Validation - Connection rules and constraints
- Subflows - Hierarchical node organization
- Performance - Large dataset handling
- Accessibility - Keyboard navigation and screen readers
Run the examples locally:
bun startSome pre-requisites before install dependencies:
- Install Node Version Manager (NVM)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash - Install Bun
curl -fsSL https://bun.sh/install | bash
nvm use
bun installbun startbun run lint # checks source for lint violations
bun run format # checks source for format violations
bun run lint:fix # fixes lint violations
bun run format:fix # fixes format violationsThe only requirements when contributing are:
- You keep a clean git history in your branch
- rebasing
maininstead of making merge commits.
- rebasing
- Using proper commit message formats that adhere to conventional commits
- Additionally, squashing (via rebase) commits that are not conventional commits
- CI checks pass before merging into
main