It is an element that you can connect to via edges. They are positioned on the viewport using x and y coords and are positioned relatively to where the middle of the viewport is. You can connect to React Flow Nodes by using handlers.

Custom Nodes
You can (and probably should) build custom nodes as the one provided by default are too simple for more advanced use-cases. The most common use-cases are:
- Multiple handles with different outputs
- Data Visualisation
- Form elements
Example
// textUpdaterNode.tsx
import { Handle, NodeProps, Position } from '@xyflow/react';
import { TextUpdatedNode } from './types';
export function TextUpdaterNode({
data,
isConnectable,
}: NodeProps<TextUpdatedNode>) {
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
console.log({ data });
return (
<div className="p-5 bg-white rounded border">
<Handle
type="target"
id="a"
position={Position.Top}
isConnectable={isConnectable}
/>
<div>
<label htmlFor="text">Text:</label>
<input
type="text"
id="text"
name="text"
onChange={onChange}
className="nodrag"
/>
</div>
<Handle
type="source"
position={Position.Bottom}
id="b"
isConnectable={isConnectable}
/>
<Handle
isConnectable={isConnectable}
id="a"
type="source"
position={Position.Bottom}
style={{ left: 10 }}
/>
</div>
);
}
// types.ts
import type { Node, BuiltInNode } from '@xyflow/react';
export type PositionLoggerNode = Node<{ label: string }, 'position-logger'>;
export type TextUpdatedNode = Node<{ label: string }, 'text-updater'>;
export type CustomNodes = TextUpdatedNode | PositionLoggerNode;
export type AppNode = BuiltInNode | CustomNodes;
// index.ts
import type { NodeTypes } from '@xyflow/react';
import { AppNode } from './types';
import { TextUpdaterNode } from './textUpdaterNode';
export const nodeTypes = {
'text-updater': TextUpdaterNode,
} satisfies NodeTypes;
// App.tsx
const initialNodes: AppNode[] = [
{
id: 'node-3',
position: { x: 0, y: 200 },
data: { label: 'hello' },
type: 'text-updater',
},
];