Nodes
A powerful feature of React Flow is the ability to create custom nodes. This gives you the flexibility to render anything you want within your nodes. We generally recommend creating your own custom nodes rather than relying on built-in ones. With custom nodes, you can add as many source and target handles as you like—or even embed form inputs, charts, and other interactive elements.
In this section, we’ll walk through creating a custom node featuring an input field that updates text elsewhere in your application. For further examples, we recommend checking out our Custom Node Example.
Implementing a Custom Node
To create a custom node, all you need to do is create a React component. React Flow will automatically wrap it in an interactive container that injects essential props like the node’s id, position, and data, and provides functionality for selection, dragging, and connecting handles. For a full reference on all available custom node props, see the Custom Node Props.
Let’s dive into an example by creating a custom node called TextUpdaterNode
. For this, we’ve added a simple input field with a change handler. React Flow has a few handy pre-built components to simplify the process of creating custom nodes. We will use the Handle
component to allow our custom node to connect with other nodes.
function TextUpdaterNode(props) {
const onChange = useCallback((evt) => {
console.log(evt.target.value);
}, []);
return (
<div className="text-updater-node">
<div>
<label htmlFor="text">Text:</label>
<input id="text" name="text" onChange={onChange} className="nodrag" />
</div>
</div>
);
}
Adding the Node Type
You can add a new node type to React Flow by adding it to the nodeTypes
prop like below. We define the nodeTypes
outside of the component to prevent re-renderings.
const nodeTypes = {
textUpdater: TextUpdaterNode
};
function Flow() {
...
return (
<ReactFlow
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
...
/>
);
}
If nodeTypes
are defined inside a component, they must be memoized. Otherwise, React creates a new object on every render, which leads to performance issues and bugs. Here’s how you can memoize the nodeTypes
object inside a component using the useMemo
hook:
const nodeTypes = useMemo(() => ({ textUpdater: TextUpdaterNode }), []);
return <ReactFlow nodeTypes={nodeTypes} />;
After defining your new node type, you can use it by specifying the type
property on your node definition:
const nodes = [
{
id: 'node-1',
type: 'textUpdater',
position: { x: 0, y: 0 },
data: { value: 123 },
},
];
After putting all together and adding some basic styles we get a custom node that prints text to the console:
import { useState } from 'react';
import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import TextUpdaterNode from './TextUpdaterNode';
const rfStyle = {
backgroundColor: '#B8CEFF',
};
const initialNodes = [
{
id: 'node-1',
type: 'textUpdater',
position: { x: 0, y: 0 },
data: { value: 123 },
},
];
// we define the nodeTypes outside of the component to prevent re-renderings
// you could also use useMemo inside the component
const nodeTypes = { textUpdater: TextUpdaterNode };
function Flow() {
const [nodes, setNodes] = useState(initialNodes);
const [edges, setEdges] = useState([]);
return (
<ReactFlow
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
fitView
style={rfStyle}
/>
);
}
export default Flow;
To enable your custom node to connect with other nodes, check out the Handles page to learn how to add source and target handles.