Nodes

Beginner · 2 min read · Updated Nov 6, 2026

Custom Nodes in React Flow

Custom Nodes are one of the most powerful features of React Flow. They allow developers to design personalized node components with interactive forms, dynamic data, charts, buttons, and multiple connection handles.

Instead of using default node designs, you can create reusable React components that match your application's functionality and design requirements.

1. Creating a Custom Node Component

A custom node is simply a React component that defines how a node looks and behaves. You can add text, input fields, buttons, icons, and other interactive elements.

import { useCallback } from 'react';

export function TextUpdaterNode({ data }) {
  const onChange = useCallback((event) => {
    console.log(event.target.value);
  }, []);

  return (
    <div className="custom-node">
      <label>Enter Text</label>

      <input
        className="nodrag"
        defaultValue={data.label}
        onChange={onChange}
      />
    </div>
  );
}

This example creates a custom node containing an input field that responds to user changes.

2. Registering Custom Node Types

After creating your custom node component, register it using the nodeTypes object. This allows React Flow to recognize and render your custom components.

Code Example

import { TextUpdaterNode } from './TextUpdaterNode';

const nodeTypes = {
  textUpdater: TextUpdaterNode,
};

Define nodeTypes outside your main component to avoid unnecessary object recreation and improve rendering performance.

3. Adding Custom Nodes to React Flow

Once your node type is registered, assign its name to the type property of a node definition. React Flow will automatically render the corresponding custom component.

const initialNodes = [
  {
    id: '1',
    type: 'textUpdater',
    position: { x: 100, y: 100 },
    data: {
      label: 'Hello React Flow',
    },
  },
];

You can create multiple custom nodes with different IDs, positions, and data to build complex workflow diagrams.

4. Using Custom Nodes with ReactFlow

Pass your registered nodeTypes object to the <ReactFlow /> component through the nodeTypes property.

import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';

export default function App() {
  return (
    <div style={{ width: '100%', height: '500px' }}>
      <ReactFlow
        nodes={initialNodes}
        edges={[]}
        nodeTypes={nodeTypes}
        fitView
      />
    </div>
  );
}

This enables React Flow to render your custom node components inside the interactive canvas.

5. Adding Connection Handles to Custom Nodes

Handles allow custom nodes to connect with other nodes through edges. Import the Handle component and define source and target connection points.

import { Handle, Position } from '@xyflow/react';

export function CustomNode({ data }) {
  return (
    <div className="custom-node">
      <Handle
        type="target"
        position={Position.Top}
      />

      <strong>{data.label}</strong>

      <Handle
        type="source"
        position={Position.Bottom}
      />
    </div>
  );
}

You can add multiple handles and customize their positions to support advanced workflow connections.

6. Styling Custom React Flow Nodes

Custom nodes support standard CSS, allowing developers to create unique layouts, colors, borders, shadows, and interactive designs.

css
.custom-node {
  padding: 15px 20px;
  min-width: 160px;
  background: #ffffff;
  border: 1px solid #6366f1;
  border-radius: 10px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

.custom-node label {
  display: block;
  margin-bottom: 8px;
  font-weight: 600;
}

.custom-node input {
  width: 100%;
  padding: 8px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
}

CSS customization helps you build professional workflow editors that match your application's design system.

7. Updating Custom Node Data Dynamically

Custom nodes can receive dynamic data through their data property. You can use React state to update node content whenever users interact with the application.

import { useCallback } from 'react';
import {
  ReactFlow,
  useNodesState,
} from '@xyflow/react';

const initialNodes = [
  {
    id: '1',
    type: 'textUpdater',
    position: { x: 100, y: 100 },
    data: { label: 'Initial Text' },
  },
];

export default function App() {
  const [nodes, setNodes, onNodesChange] =
    useNodesState(initialNodes);

  const updateNodeData = useCallback((id, value) => {
    setNodes((nds) =>
      nds.map((node) =>
        node.id === id
          ? {
              ...node,
              data: { ...node.data, label: value },
            }
          : node
      )
    );
  }, [setNodes]);

  return (
    <ReactFlow
      nodes={nodes}
      edges={[]}
      onNodesChange={onNodesChange}
      nodeTypes={nodeTypes}
      fitView
    />
  );
}

To connect this update function to the custom node, pass it through the node's data when initializing the flow:

const nodes = [
  {
    id: '1',
    type: 'textUpdater',
    position: { x: 100, y: 100 },
    data: {
      label: 'Initial Text',
      onChange: updateNodeData,
    },
  },
];

Your custom node can then call data.onChange(id, value) when its input changes. This keeps node content synchronized with React state.

Custom NodeOpen in New Tab