react-flow-node-ts

React Flow Node Components

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "react-flow-node-ts" with this command: npx skills add sivag-lab/roth_mcp/sivag-lab-roth-mcp-react-flow-node-ts

React Flow Node Components

Patterns for building custom React Flow node components with TypeScript and Zustand.

When to Use

  • Building custom nodes for a React Flow canvas

  • Creating visual workflow / pipeline editors

  • Implementing node-based UIs with typed data

  • Adding new node types to an existing React Flow project

Quick Start

  • Copy the patterns below and replace placeholders:

  • {{NodeName}} — PascalCase component name (e.g., VideoNode )

  • {{nodeType}} — kebab-case type identifier (e.g., video-node )

  • {{NodeData}} — Data interface name (e.g., VideoNodeData )

Node Component Pattern

import { memo } from 'react'; import { Handle, Position, NodeResizer, type NodeProps } from '@xyflow/react'; import { useAppStore } from '@/store/app-store'; import type { {{NodeName}}Data } from '@/types';

type {{NodeName}}Props = NodeProps<Node<{{NodeName}}Data, '{{nodeType}}'>>;

export const {{NodeName}} = memo(function {{NodeName}}({ id, data, selected, width, height, }: {{NodeName}}Props) { const updateNode = useAppStore((s) => s.updateNode); const canvasMode = useAppStore((s) => s.canvasMode);

return ( <> <NodeResizer isVisible={selected && canvasMode === 'editing'} minWidth={200} minHeight={100} /> <div className="node-container"> <Handle type="target" position={Position.Top} />

    &#x3C;div className="node-header">
      &#x3C;span className="node-title">{data.title}&#x3C;/span>
    &#x3C;/div>

    &#x3C;div className="node-body">
      {/* Node-specific content */}
    &#x3C;/div>

    &#x3C;Handle type="source" position={Position.Bottom} />
  &#x3C;/div>
&#x3C;/>

); });

Type Definition Pattern

import type { Node } from '@xyflow/react';

// Data interface — must extend Record<string, unknown> export interface {{NodeName}}Data extends Record<string, unknown> { title: string; description?: string; // Add node-specific fields here }

// Typed node alias export type {{NodeName}} = Node<{{NodeName}}Data, '{{nodeType}}'>;

Union Type for All Nodes

export type AppNode = | Node<TextNodeData, 'text-node'> | Node<VideoNodeData, 'video-node'> | Node<{{NodeName}}Data, '{{nodeType}}'>;

Handle Configuration

// Single input, single output (most common) <Handle type="target" position={Position.Top} /> <Handle type="source" position={Position.Bottom} />

// Multiple named handles <Handle type="target" position={Position.Left} id="input-a" /> <Handle type="target" position={Position.Left} id="input-b" style={{ top: '75%' }} /> <Handle type="source" position={Position.Right} id="output" />

// Conditional handle (only show in editing mode) {canvasMode === 'editing' && ( <Handle type="source" position={Position.Bottom} /> )}

Store Integration (Zustand)

// In app-store.ts interface AppState { nodes: AppNode[]; updateNode: (id: string, data: Partial<AppNode['data']>) => void; // ... other actions }

export const useAppStore = create<AppState>((set, get) => ({ nodes: [], updateNode: (id, data) => set({ nodes: get().nodes.map((n) => n.id === id ? { ...n, data: { ...n.data, ...data } } : n ), }), }));

Default Node Data

// defaults.ts export const DEFAULT_NODE_DATA: Record<string, () => AppNode['data']> = { '{{nodeType}}': () => ({ title: 'New {{NodeName}}', description: '', }), };

Registration

// nodeTypes.ts — register all custom nodes import { {{NodeName}} } from '@/components/nodes/{{NodeName}}';

export const nodeTypes = { '{{nodeType}}': {{NodeName}}, // ... other node types } as const;

// Canvas.tsx import { ReactFlow } from '@xyflow/react'; import { nodeTypes } from './nodeTypes';

<ReactFlow nodes={nodes} edges={edges} nodeTypes={nodeTypes} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} />

Integration Steps

  • Add type — Define {{NodeName}}Data interface in types/index.ts

  • Create component — Build node in components/nodes/{{NodeName}}.tsx

  • Export — Add to components/nodes/index.ts barrel export

  • Add defaults — Register default data in store/app-store.ts

  • Register — Add to nodeTypes object for React Flow

  • Add to menus — Include in AddBlockMenu and ConnectMenu

Common Patterns

Editable Title

<input className="node-title-input" value={data.title} onChange={(e) => updateNode(id, { title: e.target.value })} onBlur={() => /* save */} />

Status Indicator

<div className={status-dot status-${data.status}} />

Validation Badge

{data.errors?.length > 0 && ( <span className="error-badge">{data.errors.length}</span> )}

Anti-Patterns

Avoid Why Instead

Heavy computation in node render Blocks canvas interaction useMemo or move to store

Inline styles for layout Inconsistent, hard to maintain CSS classes or Tailwind

Forgetting memo() wrapper Unnecessary re-renders on pan/zoom Always wrap with memo

Untyped node data Runtime errors, poor DX Always define data interface

Direct DOM manipulation Breaks React Flow internals Use React state + handles

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

react-modernization

No summary provided by upstream source.

Repository SourceNeeds Review
General

ai-engineer

No summary provided by upstream source.

Repository SourceNeeds Review
General

react-ui-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
react-flow-node-ts | V50.AI