{"slug":"react-flow-architect","title":"react-flow-architect","summary":"Build production-ready ReactFlow applications with hierarchical navigation, performance optimization, and advanced state management.","platform":"ChatGPT","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-08-17T11:41:33.641973Z","repo":{"url":"https://github.com/sickn33/agentic-awesome-skills","stars":46883,"forks":6831,"license":"MIT","updatedAt":"2026-09-25T05:43:16Z"},"bodyHtml":"<hr>\n<h2>name: react-flow-architect\ndescription: \"Build production-ready ReactFlow applications with hierarchical navigation, performance optimization, and advanced state management.\"\nrisk: critical\nsource: community\ndate_added: \"2026-02-27\"</h2>\n<h1>ReactFlow Architect</h1>\n<p>Build production-ready ReactFlow applications with hierarchical navigation, performance optimization, and advanced state management.</p>\n<h2>Quick Start</h2>\n<p>Create basic interactive graph:</p>\n<pre><code>import ReactFlow, { Node, Edge } from \"reactflow\";\n\nconst nodes: Node[] = [\n  { id: \"1\", position: { x: 0, y: 0 }, data: { label: \"Node 1\" } },\n  { id: \"2\", position: { x: 100, y: 100 }, data: { label: \"Node 2\" } },\n];\n\nconst edges: Edge[] = [{ id: \"e1-2\", source: \"1\", target: \"2\" }];\n\nexport default function Graph() {\n  return &lt;ReactFlow nodes={nodes} edges={edges} /&gt;;\n}\n</code></pre>\n<h2>Core Patterns</h2>\n<h3>Hierarchical Tree Navigation</h3>\n<p>Build expandable/collapsible tree structures with parent-child relationships.</p>\n<h4>Node Schema</h4>\n<pre><code>interface TreeNode extends Node {\n  data: {\n    label: string;\n    level: number;\n    hasChildren: boolean;\n    isExpanded: boolean;\n    childCount: number;\n    category: \"root\" | \"category\" | \"process\" | \"detail\";\n  };\n}\n</code></pre>\n<h4>Incremental Node Building</h4>\n<pre><code>const buildVisibleNodes = useCallback(\n  (allNodes: TreeNode[], expandedIds: Set&lt;string&gt;, otherDeps: any[]) =&gt; {\n    const visibleNodes = new Map&lt;string, TreeNode&gt;();\n    const visibleEdges = new Map&lt;string, TreeEdge&gt;();\n\n    // Start with root nodes\n    const rootNodes = allNodes.filter((n) =&gt; n.data.level === 0);\n\n    // Recursively add visible nodes\n    const addVisibleChildren = (node: TreeNode) =&gt; {\n      visibleNodes.set(node.id, node);\n\n      if (expandedIds.has(node.id)) {\n        const children = allNodes.filter((n) =&gt; n.parentNode === node.id);\n        children.forEach((child) =&gt; addVisibleChildren(child));\n      }\n    };\n\n    rootNodes.forEach((root) =&gt; addVisibleChildren(root));\n\n    return {\n      nodes: Array.from(visibleNodes.values()),\n      edges: Array.from(visibleEdges.values()),\n    };\n  },\n  [],\n);\n</code></pre>\n<h3>Performance Optimization</h3>\n<p>Handle large datasets with incremental rendering and memoization.</p>\n<h4>Incremental Rendering</h4>\n<pre><code>const useIncrementalGraph = (\n  allNodes: Node[],\n  allEdges: Edge[],\n  expandedList: string[],\n) =&gt; {\n  const prevExpandedListRef = useRef&lt;Set&lt;string&gt;&gt;(new Set());\n  const prevOtherDepsRef = useRef&lt;any[]&gt;([]);\n\n  const { visibleNodes, visibleEdges } = useMemo(() =&gt; {\n    const currentExpandedSet = new Set(expandedList);\n    const prevExpandedSet = prevExpandedListRef.current;\n\n    // Check if expanded list changed\n    const expandedChanged = !areSetsEqual(currentExpandedSet, prevExpandedSet);\n\n    // Check if other dependencies changed\n    const otherDepsChanged = !arraysEqual(otherDeps, prevOtherDepsRef.current);\n\n    if (expandedChanged &amp;&amp; !otherDepsChanged) {\n      // Only expanded list changed - incremental update\n      return buildIncrementalUpdate(\n        cachedVisibleNodesRef.current,\n        cachedVisibleEdgesRef.current,\n        allNodes,\n        allEdges,\n        currentExpandedSet,\n        prevExpandedSet,\n      );\n    } else {\n      // Full rebuild needed\n      return buildFullGraph(allNodes, allEdges, currentExpandedSet);\n    }\n  }, [allNodes, allEdges, expandedList, ...otherDeps]);\n\n  return { visibleNodes, visibleEdges };\n};\n</code></pre>\n<h4>Memoization Patterns</h4>\n<pre><code>// Memoize node components to prevent unnecessary re-renders\nconst ProcessNode = memo(({ data, selected }: NodeProps) =&gt; {\n  return (\n    &lt;div className={`process-node ${selected ? 'selected' : ''}`}&gt;\n      {data.label}\n    &lt;/div&gt;\n  );\n}, (prevProps, nextProps) =&gt; {\n  // Custom comparison function\n  return (\n    prevProps.data.label === nextProps.data.label &amp;&amp;\n    prevProps.selected === nextProps.selected &amp;&amp;\n    prevProps.data.isExpanded === nextProps.data.isExpanded\n  );\n});\n\n// Memoize edge calculations\nconst styledEdges = useMemo(() =&gt; {\n  return edges.map(edge =&gt; ({\n    ...edge,\n    style: {\n      ...edge.style,\n      strokeWidth: selectedEdgeId === edge.id ? 3 : 2,\n      stroke: selectedEdgeId === edge.id ? '#3b82f6' : '#94a3b8',\n    },\n    animated: selectedEdgeId === edge.id,\n  }));\n}, [edges, selectedEdgeId]);\n</code></pre>\n<h3>State Management</h3>\n<p>Complex node/edge state patterns with undo/redo and persistence.</p>\n<h4>Reducer Pattern</h4>\n<pre><code>type GraphAction =\n  | { type: \"SELECT_NODE\"; payload: string }\n  | { type: \"SELECT_EDGE\"; payload: string }\n  | { type: \"TOGGLE_EXPAND\"; payload: string }\n  | { type: \"UPDATE_NODES\"; payload: Node[] }\n  | { type: \"UPDATE_EDGES\"; payload: Edge[] }\n  | { type: \"UNDO\" }\n  | { type: \"REDO\" };\n\nconst graphReducer = (state: GraphState, action: GraphAction): GraphState =&gt; {\n  switch (action.type) {\n    case \"SELECT_NODE\":\n      return {\n        ...state,\n        selectedNodeId: action.payload,\n        selectedEdgeId: null,\n      };\n\n    case \"TOGGLE_EXPAND\":\n      const newExpanded = new Set(state.expandedNodeIds);\n      if (newExpanded.has(action.payload)) {\n        newExpanded.delete(action.payload);\n      } else {\n        newExpanded.add(action.payload);\n      }\n      return {\n        ...state,\n        expandedNodeIds: newExpanded,\n        isDirty: true,\n      };\n\n    default:\n      return state;\n  }\n};\n</code></pre>\n<h4>History Management</h4>\n<pre><code>const useHistoryManager = (\n  state: GraphState,\n  dispatch: Dispatch&lt;GraphAction&gt;,\n) =&gt; {\n  const canUndo = state.historyIndex &gt; 0;\n  const canRedo = state.historyIndex &lt; state.history.length - 1;\n\n  const undo = useCallback(() =&gt; {\n    if (canUndo) {\n      const newIndex = state.historyIndex - 1;\n      const historyEntry = state.history[newIndex];\n\n      dispatch({\n        type: \"RESTORE_FROM_HISTORY\",\n        payload: {\n          ...historyEntry,\n          historyIndex: newIndex,\n        },\n      });\n    }\n  }, [canUndo, state.historyIndex, state.history]);\n\n  const saveToHistory = useCallback(() =&gt; {\n    dispatch({ type: \"SAVE_TO_HISTORY\" });\n  }, [dispatch]);\n\n  return { canUndo, canRedo, undo, redo, saveToHistory };\n};\n</code></pre>\n<h2>Advanced Features</h2>\n<h3>Auto-Layout Integration</h3>\n<p>Integrate Dagre for automatic graph layout:</p>\n<pre><code>import dagre from \"dagre\";\n\nconst layoutOptions = {\n  rankdir: \"TB\", // Top to Bottom\n  nodesep: 100, // Node separation\n  ranksep: 150, // Rank separation\n  marginx: 50,\n  marginy: 50,\n  edgesep: 10,\n};\n\nconst applyLayout = (nodes: Node[], edges: Edge[]) =&gt; {\n  const g = new dagre.graphlib.Graph();\n  g.setGraph(layoutOptions);\n  g.setDefaultEdgeLabel(() =&gt; ({}));\n\n  // Add nodes to graph\n  nodes.forEach((node) =&gt; {\n    g.setNode(node.id, { width: 200, height: 100 });\n  });\n\n  // Add edges to graph\n  edges.forEach((edge) =&gt; {\n    g.setEdge(edge.source, edge.target);\n  });\n\n  // Calculate layout\n  dagre.layout(g);\n\n  // Apply positions\n  return nodes.map((node) =&gt; ({\n    ...node,\n    position: {\n      x: g.node(node.id).x - 100,\n      y: g.node(node.id).y - 50,\n    },\n  }));\n};\n\n// Debounce layout calculations\nconst debouncedLayout = useMemo(() =&gt; debounce(applyLayout, 150), []);\n</code></pre>\n<h3>Focus Mode</h3>\n<p>Isolate selected nodes and their direct connections:</p>\n<pre><code>const useFocusMode = (\n  selectedNodeId: string,\n  allNodes: Node[],\n  allEdges: Edge[],\n) =&gt; {\n  return useMemo(() =&gt; {\n    if (!selectedNodeId) return { nodes: allNodes, edges: allEdges };\n\n    // Get direct connections\n    const connectedNodeIds = new Set([selectedNodeId]);\n    const focusedEdges: Edge[] = [];\n\n    allEdges.forEach((edge) =&gt; {\n      if (edge.source === selectedNodeId || edge.target === selectedNodeId) {\n        focusedEdges.push(edge);\n        connectedNodeIds.add(edge.source);\n        connectedNodeIds.add(edge.target);\n      }\n    });\n\n    // Get connected nodes\n    const focusedNodes = allNodes.filter((n) =&gt; connectedNodeIds.has(n.id));\n\n    return { nodes: focusedNodes, edges: focusedEdges };\n  }, [selectedNodeId, allNodes, allEdges]);\n};\n\n// Smooth transitions for focus mode\nconst focusModeStyles = {\n  transition: \"all 0.3s ease-in-out\",\n  opacity: isInFocus ? 1 : 0.3,\n  filter: isInFocus ? \"none\" : \"blur(2px)\",\n};\n</code></pre>\n<h3>Search Integration</h3>\n<p>Search and navigate to specific nodes:</p>\n<pre><code>const searchNodes = useCallback((nodes: Node[], query: string) =&gt; {\n  if (!query.trim()) return [];\n\n  const lowerQuery = query.toLowerCase();\n  return nodes.filter(\n    (node) =&gt;\n      node.data.label.toLowerCase().includes(lowerQuery) ||\n      node.data.description?.toLowerCase().includes(lowerQuery),\n  );\n}, []);\n\nconst navigateToSearchResult = (nodeId: string) =&gt; {\n  // Expand parent nodes\n  const nodePath = calculateBreadcrumbPath(nodeId, allNodes);\n  const parentIds = nodePath.slice(0, -1).map((n) =&gt; n.id);\n\n  setExpandedIds((prev) =&gt; new Set([...prev, ...parentIds]));\n  setSelectedNodeId(nodeId);\n\n  // Fit view to node\n  fitView({ nodes: [{ id: nodeId }], duration: 800 });\n};\n</code></pre>\n<h2>Performance Tools</h2>\n<h3>Graph Performance Analyzer</h3>\n<p>Create a performance analysis script:</p>\n<pre><code>// scripts/graph-analyzer.js\nclass GraphAnalyzer {\n  analyzeCode(content, filePath) {\n    const analysis = {\n      metrics: {\n        nodeCount: this.countNodes(content),\n        edgeCount: this.countEdges(content),\n        renderTime: this.estimateRenderTime(content),\n        memoryUsage: this.estimateMemoryUsage(content),\n        complexity: this.calculateComplexity(content),\n      },\n      issues: [],\n      optimizations: [],\n      patterns: this.detectPatterns(content),\n    };\n\n    // Detect performance issues\n    this.detectPerformanceIssues(analysis);\n\n    // Suggest optimizations\n    this.suggestOptimizations(analysis);\n\n    return analysis;\n  }\n\n  countNodes(content) {\n    const nodePatterns = [\n      /nodes:\\s*\\[.*?\\]/gs,\n      /const\\s+\\w+\\s*=\\s*\\[.*?id:.*?position:/gs,\n    ];\n\n    let totalCount = 0;\n    nodePatterns.forEach((pattern) =&gt; {\n      const matches = content.match(pattern);\n      if (matches) {\n        matches.forEach((match) =&gt; {\n          const nodeMatches = match.match(/id:\\s*['\"`][^'\"`]+['\"`]/g);\n          if (nodeMatches) {\n            totalCount += nodeMatches.length;\n          }\n        });\n      }\n    });\n\n    return totalCount;\n  }\n\n  estimateRenderTime(content) {\n    const nodeCount = this.countNodes(content);\n    const edgeCount = this.countEdges(content);\n\n    // Base render time estimation (ms)\n    const baseTime = 5;\n    const nodeTime = nodeCount * 0.1;\n    const edgeTime = edgeCount * 0.05;\n\n    return baseTime + nodeTime + edgeTime;\n  }\n\n  detectPerformanceIssues(analysis) {\n    const { metrics } = analysis;\n\n    if (metrics.nodeCount &gt; 500) {\n      analysis.issues.push({\n        type: \"HIGH_NODE_COUNT\",\n        severity: \"high\",\n        message: `Too many nodes (${metrics.nodeCount}). Consider virtualization.`,\n        suggestion: \"Implement virtualization or reduce visible nodes\",\n      });\n    }\n\n    if (metrics.renderTime &gt; 16) {\n      analysis.issues.push({\n        type: \"SLOW_RENDER\",\n        severity: \"high\",\n        message: `Render time (${metrics.renderTime.toFixed(2)}ms) exceeds 60fps.`,\n        suggestion: \"Optimize with memoization and incremental rendering\",\n      });\n    }\n  }\n}\n</code></pre>\n<h2>Best Practices</h2>\n<h3>Performance Guidelines</h3>\n<ol>\n<li><strong>Use React.memo</strong> for node components to prevent unnecessary re-renders</li>\n<li><strong>Implement virtualization</strong> for graphs with 1000+ nodes</li>\n<li><strong>Debounce layout calculations</strong> during rapid interactions</li>\n<li><strong>Use useCallback</strong> for edge creation and manipulation functions</li>\n<li><strong>Implement proper TypeScript types</strong> for nodes and edges</li>\n</ol>\n<h3>Memory Management</h3>\n<pre><code>// Use Map for O(1) lookups instead of array.find\nconst nodesById = useMemo(\n  () =&gt; new Map(allNodes.map((n) =&gt; [n.id, n])),\n  [allNodes],\n);\n\n// Cache layout results\nconst layoutCacheRef = useRef&lt;Map&lt;string, Node[]&gt;&gt;(new Map());\n\n// Proper cleanup in useEffect\nuseEffect(() =&gt; {\n  return () =&gt; {\n    // Clean up any lingering references\n    nodesMapRef.current.clear();\n    edgesMapRef.current.clear();\n  };\n}, []);\n</code></pre>\n<h3>State Optimization</h3>\n<pre><code>// Use useRef for objects that shouldn't trigger re-renders\nconst autoSaveDataRef = useRef({\n  nodes: [],\n  edges: [],\n  lastSaved: Date.now(),\n});\n\n// Update properties without breaking reference\nconst updateAutoSaveData = (newNodes: Node[], newEdges: Edge[]) =&gt; {\n  autoSaveDataRef.current.nodes = newNodes;\n  autoSaveDataRef.current.edges = newEdges;\n  autoSaveDataRef.current.lastSaved = Date.now();\n};\n</code></pre>\n<h2>Common Problems &amp; Solutions</h2>\n<h3>Performance Issues</h3>\n<ul>\n<li><p><strong>Problem</strong>: Lag during node expansion</p>\n</li>\n<li><p><strong>Solution</strong>: Implement incremental rendering with change detection</p>\n</li>\n<li><p><strong>Problem</strong>: Memory usage increases over time</p>\n</li>\n<li><p><strong>Solution</strong>: Proper cleanup in useEffect hooks and use WeakMap for temporary data</p>\n</li>\n</ul>\n<h3>Layout Conflicts</h3>\n<ul>\n<li><strong>Problem</strong>: Manual positioning conflicts with auto-layout</li>\n<li><strong>Solution</strong>: Use controlled positioning state and separate layout modes</li>\n</ul>\n<h3>Rendering Issues</h3>\n<ul>\n<li><p><strong>Problem</strong>: Excessive re-renders</p>\n</li>\n<li><p><strong>Solution</strong>: Use memo, useMemo, and useCallback with stable dependencies</p>\n</li>\n<li><p><strong>Problem</strong>: Slow layout calculations</p>\n</li>\n<li><p><strong>Solution</strong>: Debounce layout calculations and cache results</p>\n</li>\n</ul>\n<h2>Complete Example</h2>\n<pre><code>import React, { useState, useCallback, useMemo, useRef } from 'react';\nimport ReactFlow, { Node, Edge, useReactFlow } from 'reactflow';\nimport dagre from 'dagre';\nimport { debounce } from 'lodash';\n\ninterface GraphState {\n  nodes: Node[];\n  edges: Edge[];\n  selectedNodeId: string | null;\n  expandedNodeIds: Set&lt;string&gt;;\n  history: GraphState[];\n  historyIndex: number;\n}\n\nexport default function InteractiveGraph() {\n  const [state, setState] = useState&lt;GraphState&gt;({\n    nodes: [],\n    edges: [],\n    selectedNodeId: null,\n    expandedNodeIds: new Set(),\n    history: [],\n    historyIndex: 0,\n  });\n\n  const { fitView } = useReactFlow();\n  const layoutCacheRef = useRef&lt;Map&lt;string, Node[]&gt;&gt;(new Map());\n\n  // Memoized styled edges\n  const styledEdges = useMemo(() =&gt; {\n    return state.edges.map(edge =&gt; ({\n      ...edge,\n      style: {\n        ...edge.style,\n        strokeWidth: state.selectedNodeId === edge.source || state.selectedNodeId === edge.target ? 3 : 2,\n        stroke: state.selectedNodeId === edge.source || state.selectedNodeId === edge.target ? '#3b82f6' : '#94a3b8',\n      },\n      animated: state.selectedNodeId === edge.source || state.selectedNodeId === edge.target,\n    }));\n  }, [state.edges, state.selectedNodeId]);\n\n  // Debounced layout calculation\n  const debouncedLayout = useMemo(\n    () =&gt; debounce((nodes: Node[], edges: Edge[]) =&gt; {\n      const cacheKey = generateLayoutCacheKey(nodes, edges);\n\n      if (layoutCacheRef.current.has(cacheKey)) {\n        return layoutCacheRef.current.get(cacheKey)!;\n      }\n\n      const layouted = applyDagreLayout(nodes, edges);\n      layoutCacheRef.current.set(cacheKey, layouted);\n\n      return layouted;\n    }, 150),\n    []\n  );\n\n  const handleNodeClick = useCallback((event: React.MouseEvent, node: Node) =&gt; {\n    setState(prev =&gt; ({\n      ...prev,\n      selectedNodeId: node.id,\n    }));\n  }, []);\n\n  const handleToggleExpand = useCallback((nodeId: string) =&gt; {\n    setState(prev =&gt; {\n      const newExpanded = new Set(prev.expandedNodeIds);\n      if (newExpanded.has(nodeId)) {\n        newExpanded.delete(nodeId);\n      } else {\n        newExpanded.add(nodeId);\n      }\n\n      return {\n        ...prev,\n        expandedNodeIds: newExpanded,\n      };\n    });\n  }, []);\n\n  return (\n    &lt;ReactFlow\n      nodes={state.nodes}\n      edges={styledEdges}\n      onNodeClick={handleNodeClick}\n      fitView\n    /&gt;\n  );\n}\n</code></pre>\n<p>This comprehensive skill provides everything needed to build production-ready ReactFlow applications with hierarchical navigation, performance optimization, and advanced state management patterns.</p>\n<h2>When to Use</h2>\n<p>This skill is applicable to execute the workflow or actions described in the overview.</p>\n<h2>Limitations</h2>\n<ul>\n<li>Use this skill only when the task clearly matches the scope described above.</li>\n<li>Do not treat the output as a substitute for environment-specific validation, testing, or expert review.</li>\n<li>Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.</li>\n</ul>\n","files":[{"path":"SKILL.md","sizeBytes":16244,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-08-17T11:43:01.563577Z","sha256":"2C2DF292CDBAB019C4C6DE0DB153F0A5A4E1FE2F6E0F73E5955E98E0FE7B0F3E","sizeBytes":5474},"review":null,"source":{"repositoryUrl":"https://github.com/sickn33/agentic-awesome-skills","path":"skills/react-flow-architect","license":"MIT","commit":"f2bba339de74414b0771234cbe4f6a15258e32a3","subtreeSha":"A0C0A4D667DA3A837370DA2A0E626E5B6F286E87B647F483959B4D8C5EE1D13D","lastSyncedAt":"2026-09-25T06:48:39.853703Z"},"reviewedAt":"2026-08-17T11:44:41.129051Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/sickn33/agentic-awesome-skills/tree/main/skills/react-flow-architect"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sickn33-agentic-awesome-skills@llmmart"},{"target":"git","command":"git clone https://github.com/sickn33/agentic-awesome-skills.git"}]}