
React Flow
- 3 installs
- 2 repo stars
- Updated August 3, 2026
- fandhe-ai/agent-reference-skills
Helps with frontend development tasks.
About
react-flow is a Claude Code skill for frontend development. It helps solo builders move faster with AI-assisted coding.
- react-flow
- Frontend Development
- AI-coding skill
React Flow by the numbers
- 3 all-time installs (skills.sh)
- Ranked #1,841 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fandhe-ai/agent-reference-skills --skill react-flowAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 2 |
| Last updated | August 3, 2026 |
| Repository | fandhe-ai/agent-reference-skills ↗ |
What it does
Helps with frontend development tasks.
Files
React Flow リファレンス
React Flow ライブラリの全 API ドキュメントを網羅したスキル。 ユーザーのタスクに応じて適切な README.md を読み、そこから個別ファイルへ辿ること。
ディレクトリ構造
.claude/skills/react-flow/
├── SKILL.md ← このファイル(エントリーポイント)
└── references/
├── learn/README.md ← 学習ガイド(25ページ)
├── troubleshooting/README.md ← トラブルシューティング(5ページ)
├── components/README.md ← コンポーネント(16ページ)
├── hooks/README.md ← フック(19ページ)
├── types/README.md ← 型定義(64ページ)
├── utils/README.md ← ユーティリティ(15ページ)
└── examples/README.md ← 実装例(8ページ)探索手順
1. ユーザーのタスクに最も関連するカテゴリを特定する 2. そのカテゴリの README.md を読む 3. README.md 内の一覧から必要な個別ファイルを選んで読む 4. 必要に応じて関連ページのリンクを辿る
カテゴリ → README.md マッピング
| タスク例 | カテゴリ | README パス |
|---|---|---|
| 基本概念、用語、フロー構築、インタラクション、カスタマイズ | learn | references/learn/README.md |
| エラー対応、マイグレーション、アトリビューション | troubleshooting | references/troubleshooting/README.md |
| ReactFlow, Background, Controls, Handle, MiniMap 等 | components | references/components/README.md |
| useReactFlow, useNodes, useEdges, useStore 等 | hooks | references/hooks/README.md |
| Node, Edge, Connection, Viewport 等の型定義 | types | references/types/README.md |
| addEdge, getBezierPath, applyNodeChanges 等 | utils | references/utils/README.md |
| ノード/エッジ/インタラクション/レイアウト等の実装例 | examples | references/examples/README.md |
Background
Renders a decorative background pattern (dots, lines, or cross grid) behind the flow canvas. Must be rendered as a child of <ReactFlow />.
Props
| Name | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique identifier; required when multiple <Background /> components are used on the same page |
variant | BackgroundVariant | BackgroundVariant.Dots | Pattern style: Dots, Lines, or Cross |
gap | `number \ | [number, number]` | 20 |
size | number | 1 (Dots/Cross), ignored (Lines) | Radius of each dot or size of each rectangle |
offset | `number \ | [number, number]` | 0 |
lineWidth | number | 1 | Stroke thickness used when drawing the pattern |
color | string | — | Color of the pattern elements |
bgColor | string | — | Background fill color |
className | string | — | CSS class applied to the container |
patternClassName | string | — | CSS class applied to the SVG pattern element |
style | CSSProperties | — | Inline styles applied to the container |
使用例
import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react';
export default function Flow() {
return (
<ReactFlow defaultNodes={[]} defaultEdges={[]}>
<Background color="#ccc" variant={BackgroundVariant.Dots} />
</ReactFlow>
);
}複数のバックグラウンドを重ねる例
<ReactFlow defaultNodes={[]} defaultEdges={[]}>
<Background id="1" gap={10} color="#f1f1f1" variant={BackgroundVariant.Lines} />
<Background id="2" gap={100} color="#ccc" variant={BackgroundVariant.Lines} />
</ReactFlow>注意点
- When using multiple
<Background />components, each must have a uniqueidprop. BackgroundVariantenum values:BackgroundVariant.Dots,BackgroundVariant.Lines,BackgroundVariant.Cross.
関連
- ./ReactFlow.md
- ./Panel.md
BaseEdge
The underlying SVG edge component used internally by all React Flow edge types. Use it inside custom edge components to get the invisible interaction area and label rendering for free.
Props
| Name | Type | Default | Description |
|---|---|---|---|
path | string | — | SVG path d attribute string defining the edge shape (e.g. generated by getBezierPath) |
labelX | number | — | X-axis position of the edge label |
labelY | number | — | Y-axis position of the edge label |
label | ReactNode | — | Label or custom element rendered along the edge |
labelStyle | CSSProperties | — | Inline styles applied to the label |
labelShowBg | boolean | — | Whether to display a background behind the label |
labelBgStyle | CSSProperties | — | Inline styles for the label background |
labelBgPadding | [number, number] | — | Padding around the label background [horizontal, vertical] |
labelBgBorderRadius | number | — | Border radius for the label background |
markerStart | string | — | SVG marker ID for the edge start point, formatted as "url(#markerId)" |
markerEnd | string | — | SVG marker ID for the edge end point, formatted as "url(#markerId)" |
interactionWidth | number | 20 | Width of the invisible interaction area around the edge for easier clicking/hovering |
...props | `Omit<SVGAttributes<SVGPathElement>, 'd' \ | 'path' \ | 'markerStart' \ |
使用例
import { BaseEdge, getStraightPath, type EdgeProps } from '@xyflow/react';
export function CustomEdge({
sourceX,
sourceY,
targetX,
targetY,
markerStart,
markerEnd,
label,
labelStyle,
interactionWidth,
}: EdgeProps) {
const [edgePath, labelX, labelY] = getStraightPath({
sourceX,
sourceY,
targetX,
targetY,
});
return (
<BaseEdge
path={edgePath}
labelX={labelX}
labelY={labelY}
label={label}
labelStyle={labelStyle}
markerStart={markerStart}
markerEnd={markerEnd}
interactionWidth={interactionWidth}
/>
);
}注意点
- Always pass
markerStartandmarkerEndthrough fromEdgePropsif you want edge markers to work. - The
interactionWidthprop creates an invisible wider hit area, improving usability on thin edges. - Use path utility functions (
getBezierPath,getStraightPath,getSmoothStepPath) to generate thepathstring. - Consult the
EdgePropstype for all properties available inside a custom edge component.
関連
- ./EdgeLabelRenderer.md
- ./EdgeText.md
- ./EdgeToolbar.md
ControlButton
A button component for adding custom actions inside the <Controls /> panel. Accepts all standard HTML button attributes.
Props
| Name | Type | Default | Description |
|---|---|---|---|
...props | ButtonHTMLAttributes<HTMLButtonElement> | — | All standard HTML <button> element attributes (onClick, className, disabled, title, etc.) |
使用例
import { ReactFlow, Controls, ControlButton } from '@xyflow/react';
export default function Flow() {
return (
<ReactFlow nodes={[]} edges={[]}>
<Controls>
<ControlButton
onClick={() => alert('Something magical just happened!')}
title="magic"
>
{/* Any icon or text */}
✨
</ControlButton>
</Controls>
</ReactFlow>
);
}注意点
- Must be rendered as a child of
<Controls />to appear in the control panel. - The component is intentionally minimal and delegates all behaviour to standard HTML button attributes.
- Child elements can be icons (e.g. from
@radix-ui/react-icons) or plain text.
関連
- ./Controls.md
Controls
Renders a control panel with zoom in/out, fit-view, and viewport lock buttons. Must be rendered as a child of <ReactFlow />.
Props
| Name | Type | Default | Description |
|---|---|---|---|
showZoom | boolean | true | Show zoom in/out buttons |
showFitView | boolean | true | Show fit-view button |
showInteractive | boolean | true | Show viewport lock/unlock toggle button |
fitViewOptions | FitViewOptionsBase<NodeType> | — | Options passed to the fit-view function |
onZoomIn | () => void | — | Callback invoked alongside the default zoom-in behaviour |
onZoomOut | () => void | — | Callback invoked alongside the default zoom-out behaviour |
onFitView | () => void | — | Callback when fit-view button is clicked; when omitted the viewport auto-adjusts to show all nodes |
onInteractiveChange | (interactiveStatus: boolean) => void | — | Callback triggered when the lock/interactive toggle is clicked |
position | PanelPosition | 'bottom-left' | Panel placement on the canvas |
orientation | `"horizontal" \ | "vertical"` | 'vertical' |
children | ReactNode | — | Additional <ControlButton /> elements |
style | CSSProperties | — | Inline styles for the container |
className | string | — | CSS class for the container |
aria-label | string | 'React Flow controls' | Accessibility label for the controls panel |
使用例
import { ReactFlow, Controls, ControlButton } from '@xyflow/react';
export default function Flow() {
return (
<ReactFlow nodes={[]} edges={[]}>
<Controls
showInteractive={false}
onFitView={() => console.log('fit view clicked')}
>
<ControlButton onClick={() => console.log('custom action')}>
+
</ControlButton>
</Controls>
</ReactFlow>
);
}注意点
- Extend the panel with custom buttons by passing
<ControlButton />as children. - TypeScript users can import the
ControlPropstype. PanelPositionvalues:'top-left','top-center','top-right','bottom-left','bottom-center','bottom-right'.
関連
- ./ControlButton.md
- ./Panel.md
- ./ReactFlow.md
EdgeLabelRenderer
A portal component that renders complex edge labels in a positioned div layer above the SVG canvas. Because edges are SVG-based, HTML elements (buttons, inputs, etc.) cannot be placed directly in an edge; this component provides an escape hatch.
Props
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | HTML content to render inside the label portal |
使用例
import { BaseEdge, EdgeLabelRenderer, getBezierPath, type EdgeProps } from '@xyflow/react';
export function CustomEdge({ id, data, ...props }: EdgeProps) {
const [edgePath, labelX, labelY] = getBezierPath(props);
return (
<>
<BaseEdge id={id} path={edgePath} />
<EdgeLabelRenderer>
<div
style={{
position: 'absolute',
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
background: '#ffcc00',
padding: 10,
borderRadius: 5,
fontSize: 12,
fontWeight: 700,
// Enable pointer events for interactive elements
pointerEvents: 'all',
}}
// Prevent the pane from panning when interacting with the label
className="nodrag nopan"
>
{data.label}
</div>
</EdgeLabelRenderer>
</>
);
}注意点
- The portal disables pointer events by default. Set
pointerEvents: 'all'on the child element to enable interaction. - Add the
nopanCSS class to interactive child elements to prevent the canvas from panning when the user interacts with them. - Add the
nodragCSS class to prevent unintended drag behaviour. - Typically paired with
getBezierPath()(or similar path utilities) to get thelabelX/labelYcoordinates. - For simple text labels, consider using
<EdgeText />instead.
関連
- ./BaseEdge.md
- ./EdgeText.md
- ./EdgeToolbar.md
EdgeText
An SVG helper component for rendering text labels inside custom edges. Handles background rendering and padding automatically.
Props
| Name | Type | Default | Description |
|---|---|---|---|
x | number | — | X position where the label should be rendered |
y | number | — | Y position where the label should be rendered |
label | ReactNode | — | Label content; typically a string but can be any React node |
labelStyle | CSSProperties | — | Inline styles applied to the label text |
labelShowBg | boolean | — | Whether to display a background rectangle behind the label |
labelBgStyle | CSSProperties | — | Inline styles for the label background rectangle |
labelBgPadding | [number, number] | — | Padding around the label background as [horizontal, vertical] |
labelBgBorderRadius | number | — | Border radius for the label background rectangle |
...props | `Omit<SVGAttributes<SVGElement>, 'x' \ | 'y'>` | — |
使用例
import { EdgeText, type EdgeProps } from '@xyflow/react';
export function CustomEdgeLabel({ label }: Pick<EdgeProps, 'label'>) {
return (
<EdgeText
x={100}
y={100}
label={label}
labelStyle={{ fill: 'white' }}
labelShowBg
labelBgStyle={{ fill: 'red' }}
labelBgPadding={[2, 4]}
labelBgBorderRadius={2}
/>
);
}注意点
- Rendered inside SVG; cannot contain arbitrary HTML. Use
<EdgeLabelRenderer />for HTML-based labels. xandyare reserved for positioning and cannot be passed via the spread...props.- The TypeScript type is exported as
EdgeTextProps.
関連
- ./BaseEdge.md
- ./EdgeLabelRenderer.md
EdgeToolbar
Renders a toolbar anchored to a specific position along an edge. Visible only when the edge is selected (unless isVisible is overridden). Does not scale with viewport zoom.
Props
| Name | Type | Default | Description |
|---|---|---|---|
edgeId | string | — | ID of the edge this toolbar is attached to (required) |
x | number | — | X position of the toolbar in flow coordinates |
y | number | — | Y position of the toolbar in flow coordinates |
isVisible | boolean | false | When true, the toolbar is visible even if the edge is not selected |
alignX | `"left" \ | "center" \ | "right"` |
alignY | `"center" \ | "top" \ | "bottom"` |
...props | HTMLAttributes<HTMLDivElement> | — | Standard HTML div element attributes |
使用例
import { memo } from 'react';
import { BaseEdge, EdgeToolbar, getBezierPath, type EdgeProps } from '@xyflow/react';
function CustomEdge(props: EdgeProps) {
const [edgePath, centerX, centerY] = getBezierPath(props);
return (
<>
<BaseEdge id={props.id} path={edgePath} />
<EdgeToolbar edgeId={props.id} x={centerX} y={centerY} isVisible>
<button onClick={() => console.log('delete', props.id)}>Delete</button>
</EdgeToolbar>
</>
);
}
export default memo(CustomEdge);注意点
- The toolbar does not scale with viewport zoom, keeping content readable at any zoom level.
- By default the toolbar is hidden unless the edge is selected; use
isVisible={true}to override this. - When multiple edges are selected simultaneously, toolbars may overlap — consider hiding them via
isVisiblein that case. - Use
getBezierPath(or similar) to compute thecenterX/centerYposition for the toolbar.
関連
- ./BaseEdge.md
- ./EdgeLabelRenderer.md
Handle
A connection point rendered on a custom node that allows edges to be connected. Handles define where edges start (source) and end (target).
Props
| Name | Type | Default | Description |
|---|---|---|---|
type | `'source' \ | 'target'` | 'source' |
position | Position | Position.Top | Placement of the handle relative to the node (Top, Right, Bottom, Left) |
id | `string \ | null` | — |
isConnectable | boolean | true | Whether connections can be made to/from this handle |
isConnectableStart | boolean | true | Whether a connection can be initiated from this handle |
isConnectableEnd | boolean | true | Whether a connection can end on this handle |
isValidConnection | IsValidConnection | — | Validation callback called when a connection is dragged onto this handle |
onConnect | OnConnect | — | Callback called when a connection is made via this handle |
...props | Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, 'id'> | — | Standard HTML div attributes (except id) |
使用例
import { Handle, Position, type NodeProps } from '@xyflow/react';
export function CustomNode({ data }: NodeProps) {
return (
<div style={{ padding: '10px 20px' }}>
<Handle type="target" position={Position.Left} />
{data.label}
<Handle type="source" position={Position.Right} />
</div>
);
}複数ハンドルの例
<Handle type="source" position={Position.Right} id="a" />
<Handle type="source" position={Position.Right} id="b" style={{ top: '75%' }} />注意点
- When a node has multiple handles of the same
type, each must have a uniqueid. - For horizontal flows, source handles typically use
Position.Rightand target handlesPosition.Left. - The
isValidConnectioncallback on<Handle />works the same as on<ReactFlow />, but for performance it is recommended to use theisValidConnectionprop on the main<ReactFlow />component instead. - TypeScript users can import
HandlePropsfor prop type definitions.
関連
- ./ReactFlow.md
- ./NodeResizer.md
- ./NodeToolbar.md
MiniMap
Renders a small overview map of the entire flow in a corner of the canvas. Optionally supports panning and zooming the main viewport by interacting with the minimap.
Props
| Name | Type | Default | Description |
|---|---|---|---|
position | PanelPosition | 'bottom-right' | Position of the minimap on the canvas |
nodeColor | `string \ | GetMiniMapNodeAttribute<Node>` | '#e2e2e2' |
nodeStrokeColor | `string \ | GetMiniMapNodeAttribute<Node>` | 'transparent' |
nodeClassName | `string \ | GetMiniMapNodeAttribute<Node>` | '' |
nodeBorderRadius | number | 5 | Border radius of node rectangles in the minimap |
nodeStrokeWidth | number | 2 | Stroke width of node rectangles in the minimap |
nodeComponent | ComponentType<MiniMapNodeProps> | — | Custom SVG component for rendering minimap nodes |
bgColor | string | — | Background fill color of the minimap |
maskColor | string | 'rgba(240, 240, 240, 0.6)' | Color of the mask that covers the non-visible viewport area |
maskStrokeColor | string | 'transparent' | Stroke color of the viewport rectangle mask |
maskStrokeWidth | number | 1 | Stroke width of the viewport rectangle mask |
pannable | boolean | false | Enable viewport panning by dragging inside the minimap |
zoomable | boolean | false | Enable viewport zooming by scrolling inside the minimap |
inversePan | boolean | — | Invert panning direction in the minimap |
zoomStep | number | 10 | Zoom step size when zooming via the minimap |
offsetScale | number | 5 | Padding offset around the flow in the minimap view |
ariaLabel | `string \ | null` | 'Mini Map' |
onClick | (event: MouseEvent, position: XYPosition) => void | — | Callback when the minimap background is clicked |
onNodeClick | (event: MouseEvent, node: Node) => void | — | Callback when a node in the minimap is clicked |
使用例
import { ReactFlow, MiniMap } from '@xyflow/react';
export default function Flow() {
return (
<ReactFlow nodes={[]} edges={[]}>
<MiniMap
pannable
zoomable
nodeColor={(node) => {
switch (node.type) {
case 'input': return '#6ede87';
case 'output': return '#6865A5';
default: return '#ff0072';
}
}}
/>
</ReactFlow>
);
}注意点
nodeComponentmust render SVG elements only (not HTML).nodeColor,nodeStrokeColor, andnodeClassNameeach accept a function(node: Node) => stringfor per-node dynamic styling.- TypeScript users can import
MiniMapPropsfor prop type definitions. PanelPositionvalues:'top-left','top-center','top-right','bottom-left','bottom-center','bottom-right'.
関連
- ./ReactFlow.md
- ./Panel.md
NodeResizeControl
A lower-level resizing control for custom nodes that gives full control over the resize handle appearance and behaviour. Use <NodeResizer /> for the standard multi-direction resizer.
Props
| Name | Type | Default | Description |
|---|---|---|---|
nodeId | string | — | ID of the node being resized (required) |
position | `ControlLinePosition \ | 'top-left' \ | 'top-right' \ |
variant | ResizeControlVariant | 'handle' | Visual variant of the control |
minWidth | number | 10 | Minimum allowed width |
minHeight | number | 10 | Minimum allowed height |
maxWidth | number | Number.MAX_VALUE | Maximum allowed width |
maxHeight | number | Number.MAX_VALUE | Maximum allowed height |
keepAspectRatio | boolean | false | Maintain aspect ratio during resize |
autoScale | boolean | true | Scale the control with viewport zoom |
resizeDirection | `'horizontal' \ | 'vertical'` | — |
shouldResize | (event: ResizeDragEvent, params: ResizeParamsWithDirection) => boolean | — | Callback to conditionally prevent a resize |
onResizeStart | OnResizeStart | — | Callback invoked when resizing begins |
onResize | OnResize | — | Callback invoked on each resize step |
onResizeEnd | OnResizeEnd | — | Callback invoked when resizing ends |
color | string | — | Color of the resize handle |
className | string | — | CSS class applied to the control |
style | CSSProperties | — | Inline styles applied to the control |
children | ReactNode | — | Custom content inside the control (e.g. an icon) |
使用例
import { memo } from 'react';
import { Handle, Position, NodeResizeControl } from '@xyflow/react';
import { ArrowsPointingOutIcon } from '@heroicons/react/24/outline';
function ResizableNode({ data, id }: { data: any; id: string }) {
return (
<>
<NodeResizeControl
nodeId={id}
minWidth={100}
minHeight={50}
position="bottom-right"
>
<ArrowsPointingOutIcon style={{ width: 12, height: 12 }} />
</NodeResizeControl>
<Handle type="target" position={Position.Left} />
<div style={{ padding: 10 }}>{data.label}</div>
<Handle type="source" position={Position.Right} />
</>
);
}
export default memo(ResizableNode);注意点
- Use this component when you need a single custom resize handle (e.g. only bottom-right corner).
- Use
<NodeResizer />when you want the full set of resize handles in all directions. shouldResizereturningfalsecancels the resize for that event.- TypeScript users can import
ResizeControlPropsfor prop type definitions.
関連
- ./NodeResizer.md
- ./Handle.md
NodeResizer
Renders draggable resize controls around a node to allow resizing in all directions. Must be placed inside a custom node component.
Props
| Name | Type | Default | Description |
|---|---|---|---|
nodeId | string | — | ID of the node being resized; inferred from context if omitted |
isVisible | boolean | true | Whether the resize controls are visible |
minWidth | number | 10 | Minimum allowed width |
minHeight | number | 10 | Minimum allowed height |
maxWidth | number | Number.MAX_VALUE | Maximum allowed width |
maxHeight | number | Number.MAX_VALUE | Maximum allowed height |
keepAspectRatio | boolean | false | Maintain aspect ratio during resize |
autoScale | boolean | true | Scale the controls with viewport zoom |
color | string | — | Color of the resize handles |
handleClassName | string | — | CSS class applied to handle elements |
handleStyle | CSSProperties | — | Inline styles applied to handle elements |
lineClassName | string | — | CSS class applied to resize line elements |
lineStyle | CSSProperties | — | Inline styles applied to resize line elements |
shouldResize | (event: ResizeDragEvent, params: ResizeParamsWithDirection) => boolean | — | Callback to conditionally prevent a resize |
onResizeStart | OnResizeStart | — | Callback invoked when resizing begins |
onResize | OnResize | — | Callback invoked on each resize step |
onResizeEnd | OnResizeEnd | — | Callback invoked when resizing ends |
使用例
import { memo } from 'react';
import { Handle, Position, NodeResizer } from '@xyflow/react';
function ResizableNode({ data }: { data: { label: string } }) {
return (
<>
<NodeResizer minWidth={100} minHeight={30} />
<Handle type="target" position={Position.Left} />
<div style={{ padding: 10 }}>{data.label}</div>
<Handle type="source" position={Position.Right} />
</>
);
}
export default memo(ResizableNode);注意点
- Place
<NodeResizer />as the first child inside the node component so it renders behind other content. - The node must have
style={{ width, height }}or an explicit size for resizing to work properly. - Use
<NodeResizeControl />for a single custom resize handle instead of the full set. - TypeScript users can import
NodeResizerPropsfor prop type definitions.
関連
- ./NodeResizeControl.md
- ./Handle.md
NodeToolbar
Renders a toolbar attached to a node that appears above (or around) it. Visible only when the node is selected by default. Does not scale with viewport zoom.
Props
| Name | Type | Default | Description |
|---|---|---|---|
nodeId | `string \ | string[]` | — |
isVisible | boolean | — | When true, the toolbar is visible regardless of node selection state |
position | Position | Position.Top | Where the toolbar appears relative to the node (Top, Right, Bottom, Left) |
offset | number | 10 | Spacing (px) between the node and the toolbar |
align | Align | 'center' | Alignment of the toolbar relative to the node edge ('start', 'center', 'end') |
...props | HTMLAttributes<HTMLDivElement> | — | Standard HTML div attributes |
使用例
import { memo } from 'react';
import { Handle, Position, NodeToolbar } from '@xyflow/react';
function CustomNode({ data }: { data: { label: string; toolbarVisible?: boolean } }) {
return (
<>
<NodeToolbar isVisible={data.toolbarVisible} position={Position.Top}>
<button>Delete</button>
<button>Copy</button>
<button>Expand</button>
</NodeToolbar>
<div style={{ padding: '10px 20px' }}>{data.label}</div>
<Handle type="target" position={Position.Left} />
<Handle type="source" position={Position.Right} />
</>
);
}
export default memo(CustomNode);注意点
- The toolbar is hidden by default and only shown when the node is selected.
- When multiple nodes are selected, the toolbar automatically hides to avoid visual clutter.
- Set
isVisible={true}to force the toolbar to always be visible regardless of selection. - The toolbar does not scale with viewport zoom, keeping its content readable at any zoom level.
- Pass an array to
nodeIdto attach one toolbar to a group of nodes.
関連
- ./Handle.md
- ./NodeResizer.md
- ./Panel.md
Panel
Positions content as an overlay above the flow canvas. Used internally by <MiniMap /> and <Controls />. Accepts all standard HTML div attributes.
Props
| Name | Type | Default | Description |
|---|---|---|---|
position | PanelPosition | 'top-left' | Corner or edge where the panel is placed |
...props | DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> | — | All standard HTML div attributes (style, className, onClick, etc.) |
Available Positions
| Value | Description |
|---|---|
'top-left' | Top-left corner |
'top-center' | Top edge, centered |
'top-right' | Top-right corner |
'bottom-left' | Bottom-left corner |
'bottom-center' | Bottom edge, centered |
'bottom-right' | Bottom-right corner |
'center-left' | Left edge, centered vertically |
'center-right' | Right edge, centered vertically |
使用例
import { ReactFlow, Panel } from '@xyflow/react';
export default function Flow() {
return (
<ReactFlow nodes={[]} edges={[]}>
<Panel position="top-left">
<button onClick={() => console.log('action')}>Custom Action</button>
</Panel>
<Panel position="bottom-right" style={{ background: 'white', padding: 8 }}>
Legend
</Panel>
</ReactFlow>
);
}注意点
- The
Panelcomponent renders in a div layer above the canvas, not inside the SVG. - Child elements are rendered as normal HTML, so full CSS and interactivity is supported.
- TypeScript users can import
PanelPropsfor prop type definitions.
関連
- ./Controls.md
- ./MiniMap.md
- ./ReactFlow.md
ReactFlow
The main React Flow component that renders the interactive node-based graph. It wraps the viewport, nodes, edges, and all built-in UI elements.
Props
Common Props
| Name | Type | Default | Description |
|---|---|---|---|
nodes | Node[] | [] | Array of nodes for controlled flow |
edges | Edge[] | [] | Array of edges for controlled flow |
defaultNodes | Node[] | — | Initial nodes for uncontrolled flow |
defaultEdges | Edge[] | — | Initial edges for uncontrolled flow |
nodeTypes | NodeTypes | Built-in types | Custom node component mapping |
edgeTypes | EdgeTypes | Built-in types | Custom edge component mapping |
width | number | — | Sets fixed flow width |
height | number | — | Sets fixed flow height |
nodeOrigin | NodeOrigin | [0, 0] | Node placement origin point |
paneClickDistance | number | 0 | Mouse movement threshold triggering click |
nodeClickDistance | number | 0 | Node click distance threshold |
nodeDragThreshold | number | 1 | Pixels required before drag triggers |
connectionDragThreshold | number | 1 | Pixels before connection line appears |
colorMode | ColorMode | 'light' | Controls styling color scheme |
debug | boolean | false | Logs debug info to console |
ariaLabelConfig | Partial<AriaLabelConfig> | — | Customizable accessibility labels |
proOptions | ProOptions | — | Pro subscription options |
autoPanOnNodeFocus | boolean | true | Auto-pan viewport when nodes focused |
Viewport Props
| Name | Type | Default | Description |
|---|---|---|---|
defaultViewport | Viewport | {x:0, y:0, zoom:1} | Initial viewport position/zoom |
viewport | Viewport | — | Controlled viewport state |
onViewportChange | (viewport: Viewport) => void | — | Handler for viewport updates |
fitView | boolean | — | Auto-fit all nodes initially |
fitViewOptions | FitViewOptionsBase | — | Customize initial fit behavior |
minZoom | number | 0.5 | Minimum zoom level |
maxZoom | number | 2 | Maximum zoom level |
snapToGrid | boolean | — | Enable grid snapping |
snapGrid | SnapGrid | — | Grid snap configuration |
onlyRenderVisibleElements | boolean | false | Render only viewport-visible items |
translateExtent | CoordinateExtent | Infinite | Viewport boundary limits |
nodeExtent | CoordinateExtent | — | Node placement boundary limits |
preventScrolling | boolean | true | Block page scroll over flow |
attributionPosition | PanelPosition | 'bottom-right' | Attribution location |
Edge Props
| Name | Type | Default | Description |
|---|---|---|---|
elevateEdgesOnSelect | boolean | false | Raise z-index of selected edges |
defaultMarkerColor | `string \ | null` | '#b1b1b7' |
defaultEdgeOptions | DefaultEdgeOptions | — | Default settings for new edges |
reconnectRadius | number | 10 | Connection trigger radius |
edgesReconnectable | boolean | true | Allow edge source/target updates |
Interaction Props
| Name | Type | Default | Description |
|---|---|---|---|
nodesDraggable | boolean | true | Global node drag enablement |
nodesConnectable | boolean | true | Global connection enablement |
nodesFocusable | boolean | true | Tab/Enter focus cycling |
edgesFocusable | boolean | true | Edge focus cycling |
elementsSelectable | boolean | true | Click-based selection |
autoPanOnConnect | boolean | true | Auto-pan during connection creation |
autoPanOnNodeDrag | boolean | true | Auto-pan during node drag |
autoPanSpeed | number | 15 | Auto-pan speed |
panOnDrag | `boolean \ | number[]` | true |
selectionOnDrag | boolean | false | Multi-select without modifier key |
selectionMode | SelectionMode | 'full' | Selection box behavior ('partial' or 'full') |
panOnScroll | boolean | false | Scroll-based panning |
panOnScrollSpeed | number | 0.5 | Scroll pan speed |
panOnScrollMode | PanOnScrollMode | 'free' | Scroll pan direction restriction |
zoomOnScroll | boolean | true | Scroll-based zoom |
zoomOnPinch | boolean | true | Touch pinch zoom |
zoomOnDoubleClick | boolean | true | Double-click zoom |
selectNodesOnDrag | boolean | true | Node selection during drag |
elevateNodesOnSelect | boolean | true | Raise z-index of selected nodes |
connectOnClick | boolean | true | Click-based connection creation |
connectionMode | ConnectionMode | 'strict' | Connection validation mode |
zIndexMode | ZIndexMode | 'basic' | Z-index calculation behavior |
Connection Line Props
| Name | Type | Default | Description |
|---|---|---|---|
connectionLineStyle | CSSProperties | — | Connection line styling |
connectionLineType | ConnectionLineType | Bezier | Connection line path type |
connectionRadius | number | 20 | Handle drop radius |
connectionLineComponent | ConnectionLineComponent | — | Custom connection line component |
connectionLineContainerStyle | CSSProperties | — | Container styling |
Keyboard Props
| Name | Type | Default | Description |
|---|---|---|---|
deleteKeyCode | `KeyCode \ | null` | 'Backspace' |
selectionKeyCode | `KeyCode \ | null` | 'Shift' |
multiSelectionKeyCode | `KeyCode \ | null` | Platform-dependent |
zoomActivationKeyCode | `KeyCode \ | null` | Platform-dependent |
panActivationKeyCode | `KeyCode \ | null` | 'Space' |
disableKeyboardA11y | boolean | false | Disable keyboard accessibility |
Style Props
| Name | Type | Default | Description |
|---|---|---|---|
noPanClassName | string | 'nopan' | Class preventing panning |
noDragClassName | string | 'nodrag' | Class preventing dragging |
noWheelClassName | string | 'nowheel' | Class preventing wheel zoom |
General Event Handlers
| Name | Type | Description |
|---|---|---|
onError | OnError | Called when React Flow throws errors |
onInit | (instance: ReactFlowInstance) => void | Fires when viewport initializes |
onDelete | OnDelete | Called when nodes/edges deleted |
onBeforeDelete | OnBeforeDelete | Pre-deletion handler; can abort/modify |
Node Event Handlers
| Name | Type | Description |
|---|---|---|
onNodeClick | NodeMouseHandler | Node click handler |
onNodeDoubleClick | NodeMouseHandler | Node double-click handler |
onNodeDragStart | OnNodeDrag | Drag initiation handler |
onNodeDrag | OnNodeDrag | Active drag handler |
onNodeDragStop | OnNodeDrag | Drag completion handler |
onNodeMouseEnter | NodeMouseHandler | Mouse enter handler |
onNodeMouseMove | NodeMouseHandler | Mouse move handler |
onNodeMouseLeave | NodeMouseHandler | Mouse leave handler |
onNodeContextMenu | NodeMouseHandler | Right-click handler |
onNodesDelete | OnNodesDelete | Node deletion handler |
onNodesChange | OnNodesChange | Controlled flow update handler |
Edge Event Handlers
| Name | Type | Description |
|---|---|---|
onEdgeClick | (event: MouseEvent, edge: Edge) => void | Edge click handler |
onEdgeDoubleClick | EdgeMouseHandler | Edge double-click handler |
onEdgeMouseEnter | EdgeMouseHandler | Mouse enter handler |
onEdgeMouseMove | EdgeMouseHandler | Mouse move handler |
onEdgeMouseLeave | EdgeMouseHandler | Mouse leave handler |
onEdgeContextMenu | EdgeMouseHandler | Right-click handler |
onReconnect | OnReconnect | Edge reconnection handler |
onReconnectStart | (event: MouseEvent, edge: Edge, type: string) => void | Reconnection start handler |
onReconnectEnd | (event: MouseEvent, edge: Edge, type: string, state: ReconnectState) => void | Reconnection end handler |
onEdgesDelete | OnEdgesDelete | Edge deletion handler |
onEdgesChange | OnEdgesChange | Controlled flow update handler |
Connection Event Handlers
| Name | Type | Description |
|---|---|---|
onConnect | OnConnect | New connection completion handler |
onConnectStart | OnConnectStart | Connection drag initiation |
onConnectEnd | OnConnectEnd | Connection drag completion |
onClickConnectStart | OnConnectStart | Click-to-connect initiation |
onClickConnectEnd | OnConnectEnd | Click-to-connect completion |
isValidConnection | IsValidConnection | Connection validation callback |
Pane Event Handlers
| Name | Type | Description |
|---|---|---|
onMove | OnMove | Pan/zoom in progress |
onMoveStart | OnMove | Pan/zoom initiation |
onMoveEnd | OnMove | Pan/zoom completion |
onPaneClick | (event: MouseEvent) => void | Pane click handler |
onPaneContextMenu | (event: MouseEvent) => void | Pane right-click handler |
onPaneScroll | (event?: WheelEvent) => void | Pane scroll handler |
onPaneMouseMove | (event: MouseEvent) => void | Pane mouse move handler |
onPaneMouseEnter | (event: MouseEvent) => void | Pane mouse enter handler |
onPaneMouseLeave | (event: MouseEvent) => void | Pane mouse leave handler |
Selection Event Handlers
| Name | Type | Description |
|---|---|---|
onSelectionChange | OnSelectionChangeFunc | Selection group change handler |
onSelectionDragStart | SelectionDragHandler | Selection box drag start |
onSelectionDrag | SelectionDragHandler | Selection box drag in progress |
onSelectionDragStop | SelectionDragHandler | Selection box drag completion |
onSelectionStart | (event: MouseEvent) => void | Selection initiation handler |
onSelectionEnd | (event: MouseEvent) => void | Selection completion handler |
onSelectionContextMenu | (event: MouseEvent, nodes: Node[]) => void | Selection right-click handler |
使用例
import { useCallback } from 'react';
import {
ReactFlow,
addEdge,
useNodesState,
useEdgesState,
type OnConnect,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const initialNodes = [
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } },
{ id: '2', position: { x: 0, y: 100 }, data: { label: 'Node 2' } },
];
const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];
export default function Flow() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect: OnConnect = useCallback(
(connection) => setEdges((eds) => addEdge(connection, eds)),
[setEdges],
);
return (
<div style={{ width: '100vw', height: '100vh' }}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
/>
</div>
);
}注意点
- Event handlers (
onNodesChange,onConnectetc.) must be defined outside the component or wrapped withuseCallback. Failing to do so can cause an infinite re-render loop. - Pass
nullto keyboard props (e.g.deleteKeyCode={null}) to disable specific shortcuts. - Individual node/edge properties override the corresponding global props set on
<ReactFlow />. - The
<ReactFlow />container must have an explicit width and height (e.g. via CSS or inline styles). - Props are exported as the
ReactFlowPropstype for TypeScript users.
関連
- ./ReactFlowProvider.md
- ./Background.md
- ./Controls.md
- ./MiniMap.md
- ./Panel.md
ReactFlowProvider
A React context provider that makes the React Flow store accessible to child components outside the <ReactFlow /> component. Required when accessing flow state (via hooks) from components that are not rendered inside <ReactFlow />.
Props
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Child components wrapped by the provider |
initialNodes | Node[] | — | Initial nodes used to initialize the flow (not dynamic) |
initialEdges | Edge[] | — | Initial edges used to initialize the flow (not dynamic) |
defaultNodes | Node[] | — | Alias for initialNodes; used to initialize the flow (not dynamic) |
defaultEdges | Edge[] | — | Alias for initialEdges; used to initialize the flow (not dynamic) |
initialWidth | number | — | Initial width; required for fitView to work on the server |
initialHeight | number | — | Initial height; required for fitView to work on the server |
fitView | boolean | — | Auto-zoom and pan to fit all initially provided nodes |
initialFitViewOptions | FitViewOptionsBase<NodeType> | — | Customization options for the initial fitView behavior |
initialMinZoom | number | — | Starting minimum zoom level |
initialMaxZoom | number | — | Starting maximum zoom level |
nodeOrigin | NodeOrigin | [0, 0] | The origin of the node used when placing it or looking up its x/y position |
nodeExtent | CoordinateExtent | — | Constrains node placement within specified boundaries |
zIndexMode | ZIndexMode | — | Controls z-index layering behavior |
使用例
import { ReactFlow, ReactFlowProvider, useNodes } from '@xyflow/react';
export default function Flow() {
return (
<ReactFlowProvider>
<ReactFlow nodes={[]} edges={[]} />
<Sidebar />
</ReactFlowProvider>
);
}
function Sidebar() {
// useNodes is accessible here because Sidebar is inside ReactFlowProvider
const nodes = useNodes();
return (
<aside>
{nodes.map((node) => (
<div key={node.id}>
Node {node.id} — x: {node.position.x.toFixed(2)}, y:{' '}
{node.position.y.toFixed(2)}
</div>
))}
</aside>
);
}注意点
- When using a router, place
<ReactFlowProvider />outside of your router component so the flow state persists across route changes. - When rendering multiple flows on the same page, each flow must be wrapped in its own
<ReactFlowProvider />. <ReactFlow />itself implicitly includes a provider;<ReactFlowProvider />is only needed when you need to access the store from sibling or ancestor components.
関連
- ./ReactFlow.md
React Flow — Components
| Name | Description | Path |
|---|---|---|
<ReactFlow> | ノードとエッジを描画するメインコンポーネント | ./ReactFlow.md |
<ReactFlowProvider> | React Flow のコンテキストを提供するプロバイダー | ./ReactFlowProvider.md |
<Background> | ドット・ライン・クロスパターンの背景を描画 | ./Background.md |
<BaseEdge> | カスタムエッジの基盤となる SVG パスコンポーネント | ./BaseEdge.md |
<ControlButton> | Controls パネル内に追加するカスタムボタン | ./ControlButton.md |
<Controls> | ズーム・フィット・ロック操作のコントロールパネル | ./Controls.md |
<EdgeLabelRenderer> | エッジ上に HTML ラベルを描画するレンダラー | ./EdgeLabelRenderer.md |
<EdgeText> | エッジ上にテキストラベルを描画する SVG コンポーネント | ./EdgeText.md |
<EdgeToolbar> | エッジにフローティングツールバーを表示 | ./EdgeToolbar.md |
<Handle> | ノードの接続ポイント(ソース/ターゲット) | ./Handle.md |
<MiniMap> | フロー全体のミニマップを表示 | ./MiniMap.md |
<NodeResizeControl> | ノードのリサイズハンドルを提供するコントロール | ./NodeResizeControl.md |
<NodeResizer> | ノードのリサイズ機能を追加するラッパー | ./NodeResizer.md |
<NodeToolbar> | ノードにフローティングツールバーを表示 | ./NodeToolbar.md |
<Panel> | ビューポート上に固定位置のパネルを配置 | ./Panel.md |
<ViewportPortal> | ビューポート座標系に HTML 要素を配置するポータル | ./ViewportPortal.md |
ViewportPortal
Renders child elements inside the flow viewport coordinate system, making them move and scale along with the canvas when panning and zooming.
Props
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Content to render within the viewport coordinate space |
使用例
import { ReactFlow, ViewportPortal } from '@xyflow/react';
export default function Flow() {
return (
<ReactFlow nodes={[]} edges={[]}>
<ViewportPortal>
{/* This div is positioned at flow coordinates (100, 100) */}
<div
style={{
position: 'absolute',
transform: 'translate(100px, 100px)',
}}
>
I move and zoom with the canvas
</div>
</ViewportPortal>
</ReactFlow>
);
}注意点
- Child elements share the same coordinate system as nodes and edges, so
transform: translate(x, y)uses flow-space units. - Unlike
<Panel />, content rendered viaViewportPortalis affected by pan and zoom operations. - Use
position: 'absolute'on child elements so coordinates are relative to the viewport origin. - Suitable for custom annotations, watermarks, or decorative elements that should track the canvas.
関連
- ./Panel.md
- ./EdgeLabelRenderer.md
- ./ReactFlow.md
Edges Examples
Custom Edges
削除ボタン付きエッジ、双方向エッジ、セルフ接続エッジの3種類のカスタムエッジ実装例。
コード例
// エッジタイプの登録
const edgeTypes = {
bidirectional: BiDirectionalEdge,
selfconnecting: SelfConnectingEdge,
buttonedge: ButtonEdge,
};
// 削除ボタン付きエッジ
export default function ButtonEdge({
id, sourceX, sourceY, targetX, targetY,
sourcePosition, targetPosition, markerEnd,
}: EdgeProps) {
const [edgePath, labelX, labelY] = getBezierPath({
sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition,
});
const { setEdges } = useReactFlow();
const onEdgeClick = () =>
setEdges((edges) => edges.filter((edge) => edge.id !== id));
return (
<>
<BaseEdge path={edgePath} markerEnd={markerEnd} />
<EdgeLabelRenderer>
<button
style={{ transform: `translate(-50%,-50%) translate(${labelX}px,${labelY}px)` }}
onClick={onEdgeClick}
>
×
</button>
</EdgeLabelRenderer>
</>
);
}ポイント
edgeTypesオブジェクトでカスタムエッジを登録してReactFlowに渡すBaseEdgeでパスとマーカーを簡単にレンダリングできるEdgeLabelRendererでインタラクティブなコントロールをエッジ上に配置できる- 双方向エッジは
useStoreで対向エッジを検出してパスの曲率を調整 - セルフ接続エッジはカスタムの二次ベジェパスを使用
---
Edge Types
React Flow の4種類の組み込みエッジタイプ: default (bezier)、straight、step、smoothstep。
コード例
const initialEdges = [
{
id: '1',
type: 'straight',
source: '1',
target: '2',
label: 'straight',
},
{
id: '2',
type: 'step',
source: '2',
target: '3',
label: 'step',
},
{
id: '3',
type: 'smoothstep',
source: '3',
target: '4',
label: 'smoothstep',
},
{
id: '4',
type: 'bezier',
source: '4',
target: '5',
label: 'bezier',
},
];ポイント
typeプロパティでエッジの外観を指定する- 同一フロー内で異なるエッジタイプを混在させられる
labelプロパティでエッジにテキストラベルを付けられる
---
Edge Label Renderer
HTML divをSVGエッジの上にレンダリングするポータルコンポーネント。SVGの制約を超えたリッチなインタラクションが可能になる。
コード例
import { EdgeLabelRenderer, BaseEdge, getBezierPath } from '@xyflow/react';
const CustomEdge: FC<EdgeProps<Edge<{ label: string }>>> = ({
id, sourceX, sourceY, targetX, targetY,
sourcePosition, targetPosition, data,
}) => {
const [edgePath, labelX, labelY] = getBezierPath({
sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition,
});
return (
<>
<BaseEdge id={id} path={edgePath} />
<EdgeLabelRenderer>
<div
style={{
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
pointerEvents: 'all',
}}
>
{data.label}
</div>
</EdgeLabelRenderer>
</>
);
};ポイント
<EdgeLabelRenderer>はポータルとして動作し、SVG外に子要素をレンダリングするgetBezierPath()が返す座標で CSS transform を使って正確に位置を合わせる- マウスインタラクションが必要なラベルには
pointer-events: allを設定する - 通常の HTML/CSS スタイリングが SVG の制約なしに使える
---
Edge Toolbar
エッジの上に固定表示されるツールバー。ズームに関わらずスケールが変わらない。
コード例
export function EdgeWithButton(props: EdgeProps) {
const [edgePath, centerX, centerY] = getBezierPath(props);
const { deleteElements, getEdges } = useReactFlow();
const deleteEdge = () => {
const edge = getEdges().find((e) => e.id === props.id);
if (edge) deleteElements({ edges: [edge] });
};
return (
<>
<BaseEdge id={props.id} path={edgePath} />
<EdgeToolbar edgeId={props.id} x={centerX} y={centerY} isVisible>
<button onClick={deleteEdge}>Centered Button</button>
</EdgeToolbar>
</>
);
}ポイント
getBezierPath()でエッジのセンター座標を取得するEdgeToolbarのxとyprops でビューポート基準の絶対位置を指定isVisibleprop でツールバーの表示状態を制御- ツールバーコンテンツはズームレベルに影響されない
---
Floating Edges
固定ハンドルではなく、ノードの位置に合わせて接続点が動くエッジ。
コード例
function FloatingEdge({ id, source, target, markerEnd, style }) {
const sourceNode = useInternalNode(source);
const targetNode = useInternalNode(target);
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(
sourceNode,
targetNode,
);
const [edgePath] = getBezierPath({
sourceX: sx,
sourceY: sy,
sourcePosition: sourcePos,
targetPosition: targetPos,
targetX: tx,
targetY: ty,
});
return (
<path
id={id}
className="react-flow__edge-path"
d={edgePath}
markerEnd={markerEnd}
style={style}
/>
);
}ポイント
getEdgeParams()ヘルパーでノード間の交点を計算して動的に接続位置を決定- ノードの中心から中心への直線とノード境界の交点を計算する
'floating'エッジタイプとしてedgeTypesに登録する- 接続作成中のビジュアルフィードバック用に
FloatingConnectionLineも実装する
---
Simple Floating Edges
ノードの上下左右どちらかにエッジを接続させるシンプルな実装。
コード例
// ノードの相対位置から接続側を決定
const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
const [edgePath] = getBezierPath({
sourceX: sx, sourceY: sy, sourcePosition: sourcePos,
targetX: tx, targetY: ty, targetPosition: targetPos
});ポイント
- ノード中心間の水平・垂直距離を比較してどの辺に接続するか決定
useInternalNodeでノードの内部情報とハンドル座標を取得- 固定ハンドル位置不要のフレキシブルなエッジルーティングを実現
---
Animating Edges
SVG 要素のアニメーション(animateMotion)または Web Animations API を使ったノードアニメーションの2アプローチ。
コード例
// アプローチ1: SVG animateMotion
export function AnimatedSVGEdge({
id, sourceX, sourceY, targetX, targetY,
sourcePosition, targetPosition,
}: EdgeProps) {
const [edgePath] = getSmoothStepPath({
sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition,
});
return (
<>
<BaseEdge id={id} path={edgePath} />
<circle r="10" fill="#ff0073">
<animateMotion dur="2s" repeatCount="indefinite" path={edgePath} />
</circle>
</>
);
}ポイント
- アニメーション中のノードは
draggable: falseを設定してアニメーションパスが壊れないようにする - アニメーションパスとアニメーションロジックは別々の
useEffectで管理する(エッジパス変更時のスムーズな継続のため) getSmoothStepPathまたはgetBezierPathで SVG パスを生成してアニメーションターゲットにする
---
Custom Connection Line
接続作成中に表示されるラインをカスタマイズする。ソースハンドルに応じて外観を変えることも可能。
コード例
// ConnectionLine.jsx
import { useConnection } from '@xyflow/react';
export default ({ fromX, fromY, toX, toY }) => {
const { fromHandle } = useConnection();
return (
<g>
<path
fill="none"
stroke={fromHandle.id}
strokeWidth={1.5}
d={`M${fromX},${fromY} C ${fromX} ${toY} ${fromX} ${toY} ${toX},${toY}`}
/>
<circle cx={toX} cy={toY} fill="#fff" r={3} stroke={fromHandle.id} />
</g>
);
};
// App.jsx
<ReactFlow connectionLineComponent={ConnectionLine} onConnect={onConnect} />ポイント
connectionLineComponentprop にカスタムコンポーネントを渡すuseConnection()フックでハンドル情報にアクセスしてスタイルをカスタマイズ- コンポーネントは SVG パスや要素でビジュアルフィードバックを提供する
---
Delete Edge on Drop
エッジをドラッグしてハンドルなしでペインにドロップしたとき、そのエッジを削除する。
コード例
const DeleteEdgeDrop = () => {
const edgeReconnectSuccessful = useRef(true);
const onReconnectStart = useCallback(() => {
edgeReconnectSuccessful.current = false;
}, []);
const onReconnect = useCallback((oldEdge, newConnection) => {
edgeReconnectSuccessful.current = true;
setEdges((els) => reconnectEdge(oldEdge, newConnection, els));
}, []);
const onReconnectEnd = useCallback((_, edge) => {
if (!edgeReconnectSuccessful.current) {
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
}
edgeReconnectSuccessful.current = true;
}, []);
};ポイント
useRefで再接続の成功/失敗を追跡するonReconnectStart: ドラッグ開始時に成功フラグをfalseに設定onReconnect: 有効な接続時のみフラグをtrueに設定onReconnectEnd: 再接続失敗時(フラグが false)にエッジを削除
---
Edge Intersection
ドラッグしたノードがエッジと重なったとき、そのエッジを分割してノードを中間に挿入する。
コード例
// エッジの検出 (onNodeDrag 内)
const edgeFound = document
.elementsFromPoint(centerX, centerY)
.find((el) => el.classList.contains('react-flow__edge-interaction'))
?.parentElement;
// ノードドラッグ停止時にエッジを分割
// onNodeDragStop で元エッジをドロップノードまでに再接続し、
// ドロップノードから元ターゲットへの新エッジを作成ポイント
defaultEdgeOptions: { interactionWidth: 75 }でエッジの検出領域を広げるdocument.elementsFromPoint()でドラッグ中のノード下のエッジを特定react-flow__edge-interactionクラスでエッジ要素を識別する- ドラッグ中はオーバーラップしたエッジを
stroke: 'black'でハイライト
---
Markers
エッジの端点にマーカー(矢印など)を設定する。カスタム SVG マーカーも作成可能。
コード例
const defaultEdges = [
{
id: 'A->B',
source: 'A',
target: 'B',
markerEnd: { type: MarkerType.Arrow },
label: 'default arrow',
},
{
id: 'B->G',
source: 'B',
target: 'G',
markerEnd: {
type: MarkerType.ArrowClosed,
width: 20,
height: 20,
color: '#FF0072',
},
},
];
// カスタムマーカーの参照
const activeMarkerEnd = selected ? 'url(#selected-marker)' : markerEnd;ポイント
MarkerType.ArrowとMarkerType.ArrowClosedが組み込みマーカータイプwidth/height/color/orientでマーカー外観をカスタマイズ- カスタムマーカーは SVG
<defs>内に定義して"url(#markerId)"で参照 - エッジの選択状態に応じてマーカーを動的に切り替えられる
---
Multi Connection Line
選択された複数ノードから同時に接続ラインを表示して一括接続する。
コード例
const onConnect = useCallback(
({ source, target }) => {
return setEdges((eds) =>
nodes
.filter((node) => node.id === source || node.selected)
.reduce(
(eds, node) => addEdge({ source: node.id, target }, eds),
eds,
),
);
},
[nodes],
);ポイント
onConnectでソースノードと選択されたノード全てからエッジを作成するConnectionLineコンポーネントで選択された全ノードからの接続ラインを描画getInternalNode()とinternalsSymbolを使ってハンドル境界と位置を取得(内部 API)- このハンドラがないと1本のエッジしか作成されない
---
Reconnect Edge
既存エッジをドラッグして別のハンドルに再接続できるようにする。
コード例
const onReconnect = useCallback(
(oldEdge, newConnection) =>
setEdges((els) => reconnectEdge(oldEdge, newConnection, els)),
[],
);エッジの再接続可能方向を制御:
// ソース側のみ再接続可能
{ id: '1', source: 'a', target: 'b', reconnectable: 'source' }
// ターゲット側のみ再接続可能
{ id: '2', source: 'b', target: 'c', reconnectable: 'target' }ポイント
onReconnectハンドラを定義しないとエッジはドラッグ不可reconnectEdge()ユーティリティで再接続後のエッジ状態を更新するedgesReconnectableprop (デフォルト: true) でグローバルに再接続を制御- 個別エッジの
reconnectableプロパティでbooleanまたは'source'/'target'を設定可能
---
Temporary Edges
接続ラインをリリースしたとき、ゴーストノードに繋がる未完成エッジを作成する。ユーザーが後でエッジをピックアップして接続を完成できる。
コード例
const onConnectEnd = useCallback(
(event, connectionState) => {
if (connectionState.isValid || connectionState.fromHandle.type === 'target') {
return;
}
const id = `ghost-${Date.now()}`;
const { clientX, clientY } =
'changedTouches' in event ? event.changedTouches[0] : event;
const newNode = {
id,
type: 'ghost',
position: screenToFlowPosition({ x: clientX, y: clientY }),
data: {},
};
const newEdge = {
id: `${connectionState.fromNode.id}->${id}`,
source: connectionState.fromNode.id,
target: id,
reconnectable: 'target',
};
setNodes((nodes) => nodes.concat(newNode));
setEdges((edges) => addEdge(newEdge, edges));
},
[setNodes, setEdges, screenToFlowPosition],
);ポイント
- ゴーストノードは 5x5px の不可視ノードとして接続終了点に配置される
- 一時エッジは
reconnectable: 'target'で再度ピックアップ可能にする - 再接続が完了またはエッジが削除されたとき、ゴーストノードを削除する
- タッチとマウス両方のイベントをサポート
---
Editable Edge
ドラッグ可能なコントロールポイントでエッジパスを変形できるルータブルなエッジ。
ポイント
- カスタムエッジコンポーネントとドラッグ可能なコントロールポイントで実装
- スペースキーで自由描画モードに切り替えられるカスタム接続ライン
- 中間ポイントの管理でエッジパスをリアルタイムに変形できる
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
Grouping Examples
Selection Grouping
複数ノードを選択してグループ化し、グループを解除する動的なグルーピング機能。
ポイント
- Shift + クリックで複数ノードを選択する
- 選択中にツールバーが表示され、「Group Nodes」ボタンでグループ化できる
- 選択したグループに対して「Ungroup」ボタンで子ノードを分離できる
- グループノードはリサイズ可能で、子ノードの移動に合わせてサイズが自動調整される
- グループノードは
NodeResizerを使ってリサイズ機能を実装 - Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Parent Child Relation
ノードをグループノードの上にドラッグして動的に親子関係を確立・解除する。
ポイント
- ノードをグループノードの上にドラッグすると子ノードとして付着する
- 絶対座標から親相対座標への変換が必要(座標系の変換処理が重要)
- NodeToolbar の「Detach」ボタンで親子関係を解除できる
- ドラッグ操作中は親子関係の状態を追跡して管理する
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Sub Flows
ネストされたグラフとノードのグルーピングを parentId と extent: 'parent' で実装する。
コード例
const initialNodes = [
{
id: '1',
type: 'input',
data: { label: 'Node 0' },
position: { x: 250, y: 5 },
},
{
id: '2',
data: { label: 'Group A' },
position: { x: 100, y: 100 },
style: { width: 200, height: 200 },
type: 'group',
},
{
id: '2a',
data: { label: 'Node A.1' },
position: { x: 10, y: 50 },
parentId: '2',
},
{
id: '4a',
data: { label: 'Node B.1' },
position: { x: 15, y: 65 },
parentId: '4',
extent: 'parent',
},
];ポイント
type: 'group'でコネクションハンドルなしのコンテナノードを作成するparentIdプロパティで親ノードを指定して子ノードを定義するextent: 'parent'で子ノードのドラッグを親ノードの範囲内に制限する- 多段ネスト(グループ内グループ)もサポートされている
- グループノードの
styleで幅・高さを設定する
Interaction Examples
Drag and Drop
サイドバーからノードをドラッグしてフローペインにドロップして新規ノードを作成する。HTML5 DnD API、Pointer Events API、Neodrag ライブラリの3アプローチを紹介。
コード例
// Pointer Events API アプローチ(推奨)
const onDragStart = useCallback(
(event: React.PointerEvent<HTMLDivElement>, onDrop: OnDropAction) => {
event.preventDefault();
(event.target as HTMLElement).setPointerCapture(event.pointerId);
setIsDragging(true);
setDropAction(onDrop);
},
[setIsDragging, setDropAction]
);
const onDragEnd = useCallback(
(event: PointerEvent) => {
const elementUnderPointer = document.elementFromPoint(
event.clientX,
event.clientY
);
const isDroppingOnFlow = elementUnderPointer?.closest('.react-flow');
if (isDroppingOnFlow) {
const flowPosition = screenToFlowPosition({
x: event.clientX,
y: event.clientY,
});
dropAction?.({ position: flowPosition });
}
setIsDragging(false);
},
[screenToFlowPosition, setIsDragging, dropAction]
);ポイント
screenToFlowPosition()でビューポート座標をフローキャンバス座標に変換する.react-flowコンテナ内にドロップされたか確認してからノードを作成するsetPointerCapture()でデバイスを問わず信頼性の高いドラッグトラッキングを実現- Pointer Events API はマウスとタッチのクロスデバイス互換で推奨
DnDProviderでドラッグ状態をグローバル管理しuseDnD()でアクセス
---
Context Menu
ノードを右クリックしたときにコンテキストメニューを表示し、複製・削除などの操作を提供する。
コード例
// 右クリックハンドラ
const onNodeContextMenu = useCallback(
(event, node) => {
event.preventDefault();
const pane = ref.current.getBoundingClientRect();
setMenu({
id: node.id,
top: event.clientY < pane.height - 200 && event.clientY,
left: event.clientX < pane.width - 200 && event.clientX,
right: event.clientX >= pane.width - 200 && pane.width - event.clientX,
bottom: event.clientY >= pane.height - 200 && pane.height - event.clientY,
});
},
[setMenu]
);
// メニューコンポーネント内
const duplicateNode = useCallback(() => {
const node = getNode(id);
addNodes({
...node,
id: `${node.id}-copy`,
position: { x: node.position.x + 50, y: node.position.y + 50 },
});
}, [id, getNode, addNodes]);ポイント
event.preventDefault()でブラウザのネイティブコンテキストメニューを無効化- ビューポート境界を考慮して top/left/right/bottom で位置を計算しメニューがはみ出ないようにする
onPaneClickでペインクリック時にメニューを閉じるgetNode()、addNodes()、setNodes()、setEdges()を組み合わせてノード操作を実装
---
Save and Restore
フロー図の状態を localStorage に保存し、リロード後に復元する。
コード例
const flowKey = 'example-flow';
const onSave = useCallback(() => {
if (rfInstance) {
const flow = rfInstance.toObject();
localStorage.setItem(flowKey, JSON.stringify(flow));
}
}, [rfInstance]);
const onRestore = useCallback(() => {
const restoreFlow = async () => {
const flow = JSON.parse(localStorage.getItem(flowKey));
if (flow) {
const { x = 0, y = 0, zoom = 1 } = flow.viewport;
setNodes(flow.nodes || []);
setEdges(flow.edges || []);
setViewport({ x, y, zoom });
}
};
restoreFlow();
}, [setNodes, setViewport]);ポイント
rfInstance.toObject()でノード・エッジ・ビューポートを含むシリアライズ可能なオブジェクトに変換localStorageに JSON.stringify して保存する- 復元時はノード・エッジとビューポート座標をデフォルト値付きで復元する
---
Undo and Redo
スナップショットベースのアプローチでフロー編集の undo/redo を実装する。
ポイント
- カスタム
useUndoRedoフックで過去と未来の状態スタックを管理 - 各ノード/エッジの変更時にフロー状態全体のスナップショットを作成
- Ctrl+Z(undo)と Ctrl+Shift+Z(redo)のキーボードショートカットをサポート
- ノード移動、ノード/エッジの追加・削除をトラッキング
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Copy and Paste
選択したノードとエッジをコピー・ペーストする機能の実装。
ポイント
- Shift + クリック + ドラッグで複数ノードを選択できる
- 選択ノードとそれらに繋がるエッジを一緒にコピーする
- ペースト時は適切なオフセット位置に複製要素を配置する
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Computing Flows
useNodesData、useNodeConnections、updateNodeData を使ってノード間でデータを計算・受け渡しするフローを構築する。
コード例
// UppercaseNode - 接続されたノードのテキストを大文字に変換
function UppercaseNode({ id }: NodeProps) {
const { updateNodeData } = useReactFlow();
const connections = useNodeConnections({ handleType: 'target' });
const nodesData = useNodesData<MyNode>(connections[0]?.source);
const textNode = isTextNode(nodesData) ? nodesData : null;
useEffect(() => {
updateNodeData(id, { text: textNode?.data.text.toUpperCase() });
}, [textNode]);
// ...
}ポイント
useNodesDataで接続されたノードのデータを取得するuseNodeConnectionsでハンドルタイプ指定の接続情報を取得するuseEffectで入力データの変化を監視しupdateNodeDataで出力を更新- カスタム型ガードで型安全なデータフローを実現
---
Connection Events
接続とエッジ再接続プロセス中に発火するイベントの順序を理解する。
コード例
const Flow = () => {
return (
<ReactFlow
nodes={initialNodes}
edges={initialEdges}
onConnectStart={onConnectStart}
onConnect={onConnect}
onConnectEnd={onConnectEnd}
onReconnectStart={onReconnectStart}
onReconnect={onReconnect}
onReconnectEnd={onReconnectEnd}
/>
);
};ポイント
- 新規接続フロー:
onConnectStart→onConnect→onConnectEnd - エッジ再接続フロー:
onReconnectStart→onConnectStart→onReconnect→onConnectEnd→onReconnectEnd onConnectは有効なハンドルにリリースされたときのみ発火onConnectEndは成功・失敗に関わらず常に発火する
---
Contextual Zoom
現在のズームレベルに応じてノードの表示内容を動的に切り替える。
コード例
// ズームセレクター
const zoomSelector = (s) => s.transform[2] >= 0.9;
// カスタムノード内
function ZoomNode({ data }) {
const showContent = useStore(zoomSelector);
return showContent ? data.content : <Placeholder />;
}ポイント
useStoreフックとセレクターでズーム状態を監視する(s.transform[2]がズームレベル)- ズームアウト時にスケルトン UI(グレーのバー)でビジュアル階層を維持
- 手動イベントハンドラなしにビューポート状態に応じてレスポンシブな UI を実現
---
Prevent Cycles
DFS(深さ優先探索)でグラフの循環を検出し、循環を生む接続を防ぐ。
コード例
const isValidConnection = useCallback(
(connection) => {
const nodes = getNodes();
const edges = getEdges();
const target = nodes.find((node) => node.id === connection.target);
const hasCycle = (node, visited = new Set()) => {
if (visited.has(node.id)) return false;
visited.add(node.id);
for (const outgoer of getOutgoers(node, nodes, edges)) {
if (outgoer.id === connection.source) return true;
if (hasCycle(outgoer, visited)) return true;
}
};
if (target.id === connection.source) return false;
return !hasCycle(target);
},
[getNodes, getEdges],
);ポイント
isValidConnectionprop で接続前にバリデーションを実行getOutgoers()ユーティリティで下流ノードを辿って DFS を実行useCallbackとgetNodes/getEdgesで最新のグラフ状態を参照- ソースとターゲットが同一の場合の自己ループも明示的に防ぐ
---
Touch Device
モバイルデバイスでのタッチによるノード接続をサポートする。2つのハンドルを順にタップして接続を作成する。
コード例
const TouchDeviceFlow = () => {
return (
<ReactFlow
nodes={nodes}
edges={edges}
onConnect={onConnect}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
className="touch-flow"
fitView
>
<Background />
</ReactFlow>
);
};/* ハンドルを大きくしてタッチしやすくする */
.touch-flow .react-flow__handle {
width: 20px;
height: 20px;
}ポイント
connectOnClickprop はデフォルトで有効(タッチ接続を無効化したい場合はfalseに)- ハンドルを 20x20px 以上に拡大してタッチターゲットを確保する
- 接続状態中にバウンスアニメーションでビジュアルフィードバックを提供
- 2ステップ接続: 1つ目のハンドルをタップ、次に接続先ハンドルをタップ
---
Validation
isValidConnection prop を使って、特定のノード間のみ接続を許可する。
コード例
const isValidConnection = (connection) => connection.target === 'B';
const CustomInput = () => (
<>
<div>Only connectable with B</div>
<Handle type="source" position={Position.Right} />
</>
);
<ReactFlow
nodes={nodes}
edges={edges}
onConnect={onConnect}
isValidConnection={isValidConnection}
nodeTypes={nodeTypes}
/>ポイント
isValidConnectionコールバックがtrueを返したときのみ接続が許可されるconnectionオブジェクトのtarget/source/sourceHandle/targetHandleでバリデーションを行う- カスタムノードにはドラッグ可能な
Handleコンポーネントが最低1つ必要
---
Helper Lines
ノードドラッグ時に他のノードとの水平・垂直アライメントを示すガイドラインを表示する。
ポイント
- ドラッグしたノードが他のノードと水平・垂直に揃ったとき自動検出
- ビューポートのズームやパンに応じて正確なガイドラインを表示
- スナッピング機構でノードを整列位置に自動吸着
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Collaborative
Yjs と WebSocket を使ってリアルタイムで複数ユーザーが同時に編集できるコラボレーティブフロー。
ポイント
- Yjs (CRDT) と y-websocket でリアルタイム状態同期を実装
- React Flow の状態管理と Yjs のコラボレーション技術を組み合わせる
- URL にルーム ID を含めてルームを共有する
- 右クリックでノードを追加でき、変更は接続中の全クライアントにリアルタイムで伝播
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
Layout Examples
Dagre
dagre.js を使ってツリー構造のレイアウトを自動計算する。シンプルな階層グラフのレイアウトに適している。
コード例
const getLayoutedElements = (nodes, edges, direction = 'TB') => {
const isHorizontal = direction === 'LR';
dagreGraph.setGraph({ rankdir: direction });
nodes.forEach((node) => {
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
});
edges.forEach((edge) => {
dagreGraph.setEdge(edge.source, edge.target);
});
dagre.layout(dagreGraph);
const newNodes = nodes.map((node) => {
const nodeWithPosition = dagreGraph.node(node.id);
return {
...node,
targetPosition: isHorizontal ? 'left' : 'top',
sourcePosition: isHorizontal ? 'right' : 'bottom',
position: {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - nodeHeight / 2,
},
};
});
return { nodes: newNodes, edges };
};ポイント
- 静的レイアウト: ノード/エッジ変更時は
getLayoutedElementsを再実行が必要 - dagre はノードを中心基準で配置するため、React Flow の左上基準に合わせて
width/2、height/2をオフセットする TB(上→下)とLR(左→右)の方向切り替えをサポート- ハンドルのソース/ターゲット位置もレイアウト方向に合わせて動的に設定
---
ELKjs
Eclipse Layout Kernel (ELK) を使ったより高度な階層グラフレイアウト。dagre より多くの設定オプションを持つ。
コード例
import ELK from 'elkjs/lib/elk.bundled.js';
const elk = new ELK();
const elkOptions = {
'elk.algorithm': 'layered',
'elk.layered.spacing.nodeNodeBetweenLayers': '100',
'elk.spacing.nodeNode': '80',
};
const getLayoutedElements = (nodes, edges, options = {}) => {
const isHorizontal = options?.['elk.direction'] === 'RIGHT';
const graph = {
id: 'root',
layoutOptions: options,
children: nodes.map((node) => ({
...node,
targetPosition: isHorizontal ? 'left' : 'top',
sourcePosition: isHorizontal ? 'right' : 'bottom',
width: 150,
height: 50,
})),
edges: edges,
};
return elk.layout(graph).then((layoutedGraph) => ({
nodes: layoutedGraph.children.map((node) => ({
...node,
position: { x: node.x, y: node.y },
})),
edges: layoutedGraph.edges,
}));
};ポイント
- Eclipse ドキュメント で豊富なレイアウトオプションを参照できる
- ELKjs の出力(
x,y)を React Flow のpositionプロパティに変換する - ノードの幅・高さをハードコードして ELK の計算精度を確保する
- レイアウト計算は非同期(Promise)なのでレンダリング前に await する
---
ELKjs Multiple Handles
複数のハンドル(ELK ではポートと呼ぶ)を使って ELKjs でエッジ交差を最小化したレイアウトを実現する。
コード例
// ノードデータの構造
data: {
label: 'A',
sourceHandles: [{ id: 'a-s-a' }, { id: 'a-s-b' }],
targetHandles: [{ id: 'a-t-a' }],
}
// ELK 設定
ports: [...targetPorts, ...sourcePorts],
properties: {
'org.eclipse.elk.portConstraints': 'FIXED_ORDER'
}
// ポートのサイド設定
// target: properties: { side: 'WEST' }
// source: properties: { side: 'EAST' }
const layoutOptions = {
'elk.algorithm': 'layered',
'elk.direction': 'RIGHT',
'elk.spacing.nodeNode': '40'
};ポイント
- ハンドル ID は
nodeId-source/target-idの形式で構造化する 'org.eclipse.elk.portConstraints': 'FIXED_ORDER'でポートの順序を固定する- target ポートは
side: 'WEST'、source ポートはside: 'EAST'に配置する useLayoutNodesフックで自動レイアウト計算とノード位置更新を管理
---
Horizontal
ノードの sourcePosition と targetPosition を設定してフローの方向を制御する。水平・垂直・混合レイアウトを作れる。
コード例
const initialNodes = [
{
id: 'horizontal-1',
sourcePosition: 'right',
type: 'input',
data: { label: 'Input' },
position: { x: 0, y: 80 },
},
{
id: 'horizontal-2',
sourcePosition: 'right',
targetPosition: 'left',
data: { label: 'A Node' },
position: { x: 250, y: 0 },
},
];ポイント
sourcePosition/targetPositionで各ノードのハンドル位置を制御して流れの方向を決める'left'/'right'/'top'/'bottom'を組み合わせて混合レイアウトを作れる- 異なる方向のノード間は
smoothstepエッジタイプが柔軟なパスルーティングに適している
---
Expand and Collapse
階層ツリー構造でノードの子要素を展開・折りたたむ機能を実装する。
ポイント
- カスタム
useExpandCollapseフックで表示ロジックを管理する - 元のグラフ構造を維持しつつ表示すべきノードのみをレンダリング
- ノードデータに展開/折りたたみ状態を保持する
- dagre でレイアウトを自動再計算してノードの表示変更後に位置を更新
- 動的なノード追加と自動レイアウト更新をサポート
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Auto Layout
dagre、d3-hierarchy、elkjs の3種類のレイアウトエンジンを動的に切り替えられる useAutoLayout フックの実装。
ポイント
useAutoLayoutフックは設定可能でアプリ間での再利用に設計されている- dagre、d3-hierarchy、elkjs の3アルゴリズムをランタイムで切り替え比較可能
- 実際のアプリでは1つのレイアウトエンジンのみを選んで使用する
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Force Layout
d3-force を使った物理シミュレーションベースのレイアウト。新規ノードが既存ノードと重ならないように自動配置する。
ポイント
- d3-force の物理シミュレーションでノード位置を自動最適化
- 新規追加ノードが既存ノードと重ならないことを保証
- フォース強度とノード間距離のパラメータをカスタマイズできる
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Dynamic Layouting
ユーザーがプレースホルダーをクリックしてノードを追加すると、d3-hierarchy で自動的にツリーレイアウトを再計算してノードをアニメーション付きで配置する。
ポイント
- d3-hierarchy でツリー構造のノードレイアウトを自動計算
- グラフ変更時にリアルタイムでレイアウトを再計算
- ノードは CSS/transform ベースのアニメーションで新しい位置に滑らかに移動
- エッジ上の「+」ボタンでエッジの中間にノードを挿入できる
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Node Collisions
ノードドラッグ停止時に衝突検出アルゴリズムでノードの重なりを自動解消する。
コード例
const onNodeDragStop = useCallback(() => {
setNodes((nds) =>
resolveCollisions(nds, {
maxIterations: Infinity,
overlapThreshold: 0.5,
margin: 15,
}),
);
}, [setNodes]);
// 衝突解消アルゴリズムの概要
export const resolveCollisions: CollisionAlgorithm = (
nodes,
{ maxIterations = 50, overlapThreshold = 0.5, margin = 0 },
) => {
const boxes = getBoxesFromNodes(nodes, margin);
for (let iter = 0; iter <= maxIterations; iter++) {
let moved = false;
for (let i = 0; i < boxes.length; i++) {
for (let j = i + 1; j < boxes.length; j++) {
// 重なりを検出して最小オーバーラップ軸方向に分離
}
}
if (!moved) break;
}
};ポイント
onNodeDragStopコールバックでユーザーのインタラクション後に衝突解消を実行maxIterations(反復制限)、overlapThreshold(感度)、margin(余白)を設定可能- 繰り返し処理でカスケード衝突を解消し、重なりがなくなるまで継続
- 最小オーバーラップの軸方向にノードを分離して自然な動きを実現
Misc Examples
Download Image
html-to-image ライブラリを使って React Flow ダイアグラムを PNG 画像としてエクスポートする。
コード例
import { toPng } from 'html-to-image';
import { getNodesBounds, getViewportForBounds, useReactFlow } from '@xyflow/react';
function DownloadButton() {
const { getNodes } = useReactFlow();
function onClick() {
const nodesBounds = getNodesBounds(getNodes());
const viewport = getViewportForBounds(
nodesBounds,
imageWidth,
imageHeight,
0.5, // minZoom
2 // maxZoom
);
toPng(document.querySelector('.react-flow__viewport'), {
backgroundColor: '#1a365d',
width: imageWidth,
height: imageHeight,
style: {
transform: `translate(${viewport.x}px, ${viewport.y}px) scale(${viewport.zoom})`,
},
}).then(downloadImage);
}
return (
<Panel position="top-right">
<button onClick={onClick}>Download Image</button>
</Panel>
);
}ポイント
html-to-imageは バージョン 1.11.11 に固定する。新しいバージョンには既知の画像エクスポートの問題があるgetNodesBounds()で全ノードのバウンディングボックスを計算するgetViewportForBounds()でノード全体が収まる最適なビューポート変換を計算.react-flow__viewport要素に計算したトランスフォームを適用して画像化する- ダウンロードボタンは
<Panel>コンポーネントに配置する
---
Server Side Image Creation
サーバーサイドで React Flow グラフの静的画像を生成する。Puppeteer で React Flow を SSR してスクリーンショットを撮る。
ポイント
- React Flow v12 以降で利用可能なサーバーサイドレンダリング機能を使用
- 技術スタック:
- バックエンド: Express.js サーバー
- 画像キャプチャ: Puppeteer
- 状態管理: Zustand
- CORS 有効化
- フロントエンドのインタラクティブエディターとバックエンドの画像生成サーバーを組み合わせたアーキテクチャ
- HTTP リクエストでフロー設定データをバックエンドに送信して Puppeteer でスクリーンショットを生成
- ユーザーがフローを編集するたびにリアルタイムで画像を更新
- コンテナデプロイ用の Dockerfile 設定付き
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
Nodes Examples
Custom Node
Create custom React components that function as nodes within a React Flow diagram. The classic example is a color picker node that updates the canvas background dynamically.
コード例
// Register custom node types
const nodeTypes = {
selectorNode: ColorSelectorNode,
};
// Custom node component
export default memo(({ data, isConnectable }) => {
return (
<>
<Handle type="target" position={Position.Left} />
<div>Custom Color Picker: <strong>{data.color}</strong></div>
<input
className="nodrag"
type="color"
onChange={data.onChange}
defaultValue={data.color}
/>
<Handle type="source" position={Position.Right} />
</>
);
});
// Pass to ReactFlow
<ReactFlow nodeTypes={nodeTypes} nodes={nodes} edges={edges} />ポイント
Handleコンポーネントで接続ポイントを設置する (target/source)dataprop でコールバックや状態を渡してインタラクティブにする- インタラクティブ要素には
nodragクラスを付与してドラッグを防ぐ nodeTypesオブジェクトでカスタムノードを登録してから<ReactFlow>に渡す
---
Drag Handle
特定の要素のみをドラッグハンドルとして機能させ、ノードの移動を制限する。
コード例
// ノード設定
const initialNodes = [
{
id: '2',
type: 'dragHandleNode',
dragHandle: '.drag-handle__custom',
position: { x: 200, y: 200 },
},
];
// カスタムノードコンポーネント
function DragHandleNode() {
return (
<>
<Handle type="target" position={Position.Left} />
<div className="drag-handle__label">
Only draggable here →
<span className="drag-handle__custom" />
</div>
<Handle type="source" position={Position.Right} />
</>
);
}ポイント
- ノードの
dragHandleプロパティに CSS セレクター (例:'.drag-handle__custom') を設定する - そのセレクターに合致する要素のみがドラッグ可能になる
- ドラッグハンドル内で更にドラッグを無効にしたい要素には
nodragクラスを使う
---
Node Resizer
カスタムノードにリサイズ機能を追加する。3つのアプローチを示す: 常時表示、選択時のみ表示、カスタムアイコン付き。
コード例
import { Handle, Position, NodeResizer } from '@xyflow/react';
// 基本的な NodeResizer
const ResizableNode = ({ data }) => {
return (
<>
<NodeResizer minWidth={100} minHeight={30} />
<Handle type="target" position={Position.Left} />
<div style={{ padding: 10 }}>{data.label}</div>
<Handle type="source" position={Position.Right} />
</>
);
};
// カスタムアイコン付きリサイズコントロール
const CustomNode = ({ data }) => {
return (
<>
<NodeResizeControl style={controlStyle} minWidth={100} minHeight={50}>
<ResizeIcon />
</NodeResizeControl>
<Handle type="target" position={Position.Left} />
<div>{data.label}</div>
<Handle type="source" position={Position.Right} />
</>
);
};ポイント
<NodeResizer />: デフォルトのリサイズ UI(ドラッグハンドル付き)isVisibleprop: ノード選択状態に基づいて表示を制御<NodeResizeControl />: カスタムアイコン/コントロールを使いたい場合minWidth/minHeight: リサイズの最小サイズ制約を設定
---
Node Toolbar
選択されたノードの隣にツールバーを表示するコンポーネント。ズームに関わらずスケールが変わらず常に視認できる。
コード例
function NodeWithToolbar({ data }) {
return (
<>
<NodeToolbar
isVisible={data.forceToolbarVisible || undefined}
position={data.toolbarPosition}
align={data.align}
>
<button>cut</button>
<button>copy</button>
<button>paste</button>
</NodeToolbar>
<div>{data?.label}</div>
</>
);
}ポイント
isVisible:undefinedにするとノード選択時のみ表示されるposition: ノードの上下左右に配置 (Top / Right / Bottom / Left)align: ポジションに対するアライメント (start / center / end)- ツールバーコンテンツはズームに影響されないため常にクリアに表示される
---
Add Node on Edge Drop
接続ラインをペインにドロップしたとき(有効なターゲットなし)に新しいノードを作成する。
コード例
const onConnectEnd = useCallback(
(event, connectionState) => {
if (!connectionState.isValid) {
const id = getId();
const { clientX, clientY } =
'changedTouches' in event ? event.changedTouches[0] : event;
const newNode = {
id,
position: screenToFlowPosition({ x: clientX, y: clientY }),
data: { label: `Node ${id}` },
origin: [0.5, 0.0],
};
setNodes((nds) => nds.concat(newNode));
setEdges((eds) =>
eds.concat({ id, source: connectionState.fromNode.id, target: id })
);
}
},
[screenToFlowPosition]
);ポイント
connectionState.isValidがfalseのとき、有効なターゲットなしでドロップされたことを検知screenToFlowPosition()でマウス座標をフローキャンバス座標に変換する- マウスとタッチイベント両方を
changedTouchesの確認で処理する origin: [0.5, 0]でドロップ位置にノードを中央揃えにする
---
Connection Limit
isConnectable prop を使ってハンドルの接続数を制限する。
コード例
import { Handle, useNodeConnections } from '@xyflow/react';
const CustomHandle = (props) => {
const connections = useNodeConnections({
handleType: props.type,
});
return (
<Handle
{...props}
isConnectable={connections.length < props.connectionCount}
/>
);
};
// 使用例: 接続を1つに制限
const CustomNode = () => {
return (
<div>
<CustomHandle
type="target"
position={Position.Left}
connectionCount={1}
/>
<div>← Only one edge allowed</div>
</div>
);
};ポイント
useNodeConnectionsフックでハンドルの現在の接続数を取得する- 接続数を上限と比較して
isConnectableを動的に制御する connectionCountprop で再利用可能なカスタムハンドルを作れる
---
Delete Middle Node
チェーン中間のノードを削除したとき、前後のノードを再接続して a→b→c から b 削除後に a→c にする。
コード例
const onNodesDelete = useCallback(
(deleted) => {
let remainingNodes = [...nodes];
setEdges(
deleted.reduce((acc, node) => {
const incomers = getIncomers(node, remainingNodes, acc);
const outgoers = getOutgoers(node, remainingNodes, acc);
const connectedEdges = getConnectedEdges([node], acc);
const remainingEdges = acc.filter(
(edge) => !connectedEdges.includes(edge)
);
const createdEdges = incomers.flatMap(({ id: source }) =>
outgoers.map(({ id: target }) => ({
id: `${source}->${target}`,
source,
target,
}))
);
remainingNodes = remainingNodes.filter((rn) => rn.id !== node.id);
return [...remainingEdges, ...createdEdges];
}, edges)
);
},
[nodes, edges]
);ポイント
getIncomers/getOutgoersユーティリティで削除ノードの上流・下流を特定getConnectedEdgesで削除ノードに繋がる全エッジを取得flatMapパターンで全 incomers と outgoers 間の新エッジを生成
---
Easy Connect
小さなハンドルではなくノード全体を接続ハンドルとして機能させる。
コード例
import { Handle, Position, useConnection } from '@xyflow/react';
export default function CustomNode({ id }) {
const connection = useConnection();
const isTarget = connection.inProgress && connection.fromNode.id !== id;
return (
<div className="customNode">
<div className="customNodeBody">
{!connection.inProgress && (
<Handle
className="customHandle"
position={Position.Right}
type="source"
/>
)}
{(!connection.inProgress || isTarget) && (
<Handle
className="customHandle"
position={Position.Left}
type="target"
isConnectableStart={false}
/>
)}
</div>
</div>
);
}ポイント
- ノードボディ全体を覆う不可視の
Handle(opacity: 0) を使う useConnection()フックで接続状態を監視し、ハンドルを条件付きレンダリング- ソースノードと異なるか確認してセルフ接続を防ぐ
- ノード移動を維持するために明示的なドラッグハンドルが必要
---
Intersections
ノードドラッグ中に他のノードとの重なりを検出してハイライトする。
コード例
const BasicFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const { getIntersectingNodes } = useReactFlow();
const onNodeDrag = useCallback((_: MouseEvent, node: Node) => {
const intersections = getIntersectingNodes(node).map((n) => n.id);
setNodes((ns) =>
ns.map((n) => ({
...n,
className: intersections.includes(n.id) ? 'highlight' : '',
}))
);
}, []);
return (
<ReactFlow
nodes={nodes}
edges={initialEdges}
onNodesChange={onNodesChange}
onNodeDrag={onNodeDrag}
selectNodesOnDrag={false}
>
<Background />
<Controls />
</ReactFlow>
);
};ポイント
useReactFlow()からgetIntersectingNodes()を取得するonNodeDragコールバック内で重なりを検出し、CSS クラスを動的に付与selectNodesOnDrag={false}でドラッグ中の選択を防ぎ、交差検出に集中
---
Proximity Connect
ノードが近づいたとき自動的にエッジを作成する。ドラッグ中は点線でプレビューを表示。
コード例
const MIN_DISTANCE = 150;
const getClosestEdge = useCallback((node) => {
const { nodeLookup } = store.getState();
const internalNode = getInternalNode(node.id);
const closestNode = Array.from(nodeLookup.values()).reduce(
(res, n) => {
if (n.id !== internalNode.id) {
const dx =
n.internals.positionAbsolute.x -
internalNode.internals.positionAbsolute.x;
const dy =
n.internals.positionAbsolute.y -
internalNode.internals.positionAbsolute.y;
const d = Math.sqrt(dx * dx + dy * dy);
if (d < res.distance && d < MIN_DISTANCE) {
res.distance = d;
res.node = n;
}
}
return res;
},
{ distance: Number.MAX_VALUE, node: null }
);
return closestNode.node ? createEdgeObject(closestNode.node, node) : null;
}, []);
const onNodeDrag = useCallback((_, node) => {
const closeEdge = getClosestEdge(node);
setEdges((es) => {
const nextEdges = es.filter((e) => e.className !== 'temp');
if (closeEdge && !edgeExists(nextEdges, closeEdge)) {
closeEdge.className = 'temp';
nextEdges.push(closeEdge);
}
return nextEdges;
});
}, [getClosestEdge, setEdges]);ポイント
- ユークリッド距離でノード中心間の近接度を計算する
className: 'temp'を付与した一時エッジでドラッグ中のプレビューを表示- 一時エッジは
.temp .react-flow__edge-path { stroke-dasharray: 5 5; }で点線表示 onNodeDragStopで一時エッジを確定またはクリア
---
Rotatable Node
D3 ドラッグを使って CSS transform でノードを回転させる。
コード例
import { drag } from 'd3-drag';
import { select } from 'd3-selection';
import { Handle, Position, useUpdateNodeInternals } from '@xyflow/react';
export default function RotatableNode({ id, sourcePosition, targetPosition, data }) {
const rotateControlRef = useRef(null);
const updateNodeInternals = useUpdateNodeInternals();
const [rotation, setRotation] = useState(0);
useEffect(() => {
if (!rotateControlRef.current) return;
const selection = select(rotateControlRef.current);
const dragHandler = drag().on('drag', (evt) => {
const dx = evt.x - 100;
const dy = evt.y - 100;
const rad = Math.atan2(dx, dy);
const deg = rad * (180 / Math.PI);
setRotation(180 - deg);
updateNodeInternals(id);
});
selection.call(dragHandler);
}, [id, updateNodeInternals]);
return (
<>
<div style={{ transform: `rotate(${rotation}deg)` }}>
<div ref={rotateControlRef} className="rotatable-node__handle nodrag" />
<div>{data?.label}</div>
<Handle position={sourcePosition} type="source" />
<Handle position={targetPosition} type="target" />
</div>
</>
);
}ポイント
- d3-drag と d3-selection ライブラリで回転コントロールのドラッグ処理を実装
useUpdateNodeInternals()を呼び出してノード回転時のハンドル位置を再計算- 回転コントロールに
nodragクラスを付与して React Flow のドラッグと競合を防ぐ
---
Node Position Animation
異なるグラフレイアウト間でノードを滑らかにアニメーションさせる。
ポイント
- カスタム
useAnimatedNodesフックで再利用可能なアニメーションロジックを実装 - 線形補間でノード位置をトゥイーンする
d3-timerライブラリでアニメーションタイミングを管理- 水平・垂直レイアウト切り替えをサポート
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Stress Test
大量のノードとエッジを一度にレンダリングするパフォーマンステスト。
コード例
const StressFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const updatePos = useCallback(() => {
setNodes((nds) =>
nds.map((node) => ({
...node,
position: {
x: Math.random() * 1500,
y: Math.random() * 1500,
},
}))
);
}, []);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
minZoom={0}
>
<MiniMap />
<Controls />
<Background />
<Panel position="top-right">
<button onClick={updatePos}>change pos</button>
</Panel>
</ReactFlow>
);
};ポイント
initialElements(15, 30)で 450+ ノードの大規模データセットを生成updatePosでランダムにノード位置を変更してレンダリングパフォーマンスをテストminZoom={0}で全ノードを俯瞰できるズームレベルを許可
---
Shapes
異なる幾何学的形状(円、ダイヤモンド、六角形など)を持つカスタムノードを描画する。
ポイント
- 形状タイプに基づいて異なる SVG パスを描画する統一 Shape コンポーネントを使用
- ノードデータで形状を設定:
{ type: 'shape', data: { type: 'diamond', color: '#ff0071' }} - 単一ノードタイプで複数の形状バリアントを管理
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
---
Update Node
ノードのプロパティを動的に更新する。更新のたびに新しい配列オブジェクトを作成することが必要。
コード例
const UpdateNode = () => {
const [nodes, setNodes] = useNodesState(initialNodes);
const [nodeName, setNodeName] = useState('Node 1');
useEffect(() => {
setNodes((nds) =>
nds.map((node) => {
if (node.id === '1') {
return {
...node,
data: { ...node.data, label: nodeName },
};
}
return node;
})
);
}, [nodeName, setNodes]);
};ポイント
- ノードデータを更新するにはスプレッド演算子で新しいオブジェクトを作成する(直接ミューテートは不可)
- ラベル変更、スタイル変更、ノードの表示/非表示の3パターンを示す
useEffectで状態変化を監視してノード配列を更新する
React Flow — Examples
| Name | Description | Path |
|---|---|---|
| Nodes | カスタムノード、ドラッグハンドル、リサイズ、アニメーション等(15例) | ./nodes.md |
| Edges | カスタムエッジ、フローティングエッジ、マーカー、アニメーション等(15例) | ./edges.md |
| Interaction | ドラッグ&ドロップ、コンテキストメニュー、保存/復元、Undo/Redo等(13例) | ./interaction.md |
| Grouping | 選択グルーピング、親子関係、サブフロー(3例) | ./grouping.md |
| Layout | dagre、elkjs、力学レイアウト、展開/折りたたみ等(9例) | ./layout.md |
| Styling | ベーススタイル、ダークモード、Tailwind CSS、Turbo Flow(4例) | ./styling.md |
| Whiteboard | 消しゴム、投げ縄選択、矩形描画、フリーハンド描画(4例) | ./whiteboard.md |
| Misc | 画像ダウンロード、サーバーサイド画像生成(2例) | ./misc.md |
Styling Examples
Base Style
React Flow の必須ベーススタイルシートを使ったミニマルなスタイリング実装。
コード例
import React, { useCallback } from 'react';
import {
ReactFlow,
Background,
Controls,
MiniMap,
useNodesState,
useEdgesState,
addEdge,
} from '@xyflow/react';
import '@xyflow/react/dist/base.css'; // 必須インポート
const Flow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback(
(params) => setEdges((els) => addEdge(params, els)),
[],
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
fitView
>
<Background />
<Controls />
<MiniMap />
</ReactFlow>
);
};ポイント
'@xyflow/react/dist/base.css'のインポートは必須。このスタイルシートがなければフローは正常に機能しないbase.cssはカスタマイズの土台となる最小限のスタイルのみを提供する(デフォルトテーマなし)Background、Controls、MiniMapが基本的な UI コンポーネント- デフォルトの
@xyflow/react/dist/style.cssではなくbase.cssを使うと完全なカスタムスタイリングが可能
---
Dark Mode
組み込みの colorMode prop を使ってライト・ダーク・システムの3つのカラーモードを切り替える。
コード例
const ColorModeFlow = () => {
const [colorMode, setColorMode] = useState<ColorMode>('dark');
const onChange: ChangeEventHandler<HTMLSelectElement> = (evt) => {
setColorMode(evt.target.value as ColorMode);
};
return (
<ReactFlow
nodes={nodes}
edges={edges}
colorMode={colorMode}
fitView
>
<Panel position="top-right">
<select onChange={onChange}>
<option value="dark">dark</option>
<option value="light">light</option>
<option value="system">system</option>
</select>
</Panel>
</ReactFlow>
);
};ポイント
colorModeprop に'dark'/'light'/'system'を渡すだけで切り替えできるuseState<ColorMode>()で現在のカラーモードを管理する'system'オプションはユーザーの OS レベルのカラースキーム設定を自動的に反映する
---
Tailwind
Tailwind CSS ユーティリティクラスを使って React Flow を完全にスタイリングする実装例。
コード例
import '@xyflow/react/dist/base.css'; // base.css を使用(デフォルトスタイル上書き)
const Flow = () => {
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
fitView
className="bg-teal-50"
>
<MiniMap />
<Controls />
</ReactFlow>
);
};
// カスタムノードに Tailwind を使用
const CustomNode = () => (
<div className="px-4 py-2 shadow-md rounded-md bg-white border-2 border-stone-400">
<Handle type="target" className="w-16 !bg-teal-500" position={Position.Left} />
<div>{data.label}</div>
<Handle type="source" className="w-16 !bg-teal-500" position={Position.Right} />
</div>
);ポイント
tailwind.config.jsでimportant: trueを設定して Tailwind クラスがデフォルトスタイルを上書きするようにする@xyflow/react/dist/base.cssを使うことで Tailwind クラスとの競合を最小化する<ReactFlow>のclassNameprop で背景色などを設定できる- ハンドルサイズは
!修飾子(!bg-teal-500)で重要度を上げて適用する
---
Turbo Flow
グローイングアニメーション付きグラデーションボーダーを持つノードと、グラデーションエッジで構成された視覚的に印象的なノードベース UI。turbo.build ウェブサイトのデザインを参考にした実装。
コード例
// TurboNode - グロー効果付きカスタムノード
// グラデーションボーダーのラッパー、タイトル・サブタイトル・アイコンを表示
// TurboEdge - グラデーションカラーのカスタムエッジ
// ベジェ曲線パスにグラデーションを適用
// グラデーション設定
// エッジ: linear-gradient #ae53ba (紫) → #2a8af6 (青)
// ノードボーダー: conic-gradient(選択時に回転アニメーション)
// ボックスシャドウ: 10px 0 15px rgba(42, 138, 246, 0.3)(グロー効果)
// ダークテーマ設定
// 背景: rgb(17, 17, 17)
// テキスト: rgb(243, 244, 246)
// ボーダー半径: 10px
// アニメーション
// 選択ノード: 4秒かけて360度回転するスピナーアニメーションポイント
nodeTypesとedgeTypesでカスタムコンポーネントを登録する- SVG
<defs>内にマーカーを定義してエッジエンドポイントのスタイルを設定 <Controls>を非インタラクティブモードで設定してシンプルに表示- CSS カスタム変数でノードのダークテーマカラーを一元管理する
- 選択ノードへの conic-gradient アニメーション:
@keyframesで360度回転を4秒かけて実行
Whiteboard Examples
Eraser
ノードとエッジを消しゴムでなぞるように削除するインタラクティブなツール。SVG パスとノード矩形の交差検出を使用。
コード例
export function Eraser() {
const { screenToFlowPosition, deleteElements } = useReactFlow();
const canvas = useRef<HTMLCanvasElement>(null);
const trailPoints = useRef<TimestampedPoint[]>([]);
function handlePointerDown(e: PointerEvent) {
isDrawing.current = true;
// ノード矩形とエッジサンプルポイントをキャッシュ
nodeIntersectionData.current = nodes.map((node) => ({
id: node.id,
rect: { x, y, width, height },
}));
}
function handlePointerMove(e: PointerEvent) {
// スクリーン座標をフロー座標に変換
const flowPoints = points.map(([x, y]) =>
screenToFlowPosition({ x, y })
);
// ノードとエッジの交差を確認
setNodes((nodes) =>
nodes.map((node) => ({
...node,
data: { ...node.data, toBeDeleted: intersects },
}))
);
}
function handlePointerUp(e: PointerEvent) {
deleteElements({ nodes, edges });
}
}ポイント
lineSegmentsIntersect()で線分と矩形の衝突を正確に検出する- エッジパスは SVG の
getPointAtLength()でサンプルポイントを抽出してポリラインとして交差テスト perfect-freehandライブラリで滑らかなストロークをキャンバスに描画するtoBeDeletedフラグで削除前に視覚的フィードバック(透明度低下)を提供nopan nodragクラス付きのフルスクリーンキャンバスオーバーレイでパン干渉を防止
---
Lasso Selection
フリーハンドのなげなわ形状を描いて複数ノードを選択するホワイトボードスタイルのツール。
コード例
export function Lasso({ partial }: { partial: boolean }) {
const { flowToScreenPosition, setNodes } = useReactFlow();
const { width, height, nodeLookup } = useStore();
const canvas = useRef<HTMLCanvasElement>(null);
const pointRef = useRef<[number, number][]>([]);
// ポインターイベントでなげなわパスを描画
// ctx.isPointInPath() でノードがパス内にあるか確認
// 選択ロジック: full モードは全コーナーが内側、partial モードは任意のコーナーが内側
}ポイント
- HTML5 Canvas API でなげなわパスをリアルタイム描画
ctx.isPointInPath()でどのノードがなげなわ内に含まれるか判定- partial モード: ノードの任意の部分が重なれば選択
- full モード: ノード全体がなげなわ内に完全に含まれる場合のみ選択
perfect-freehandライブラリで滑らかなストローク描画setNodes()で選択状態を更新する
---
Rectangle
ドラッグ操作でフロー上に矩形ノードを描画して追加するホワイトボードツール。
コード例
export function RectangleTool() {
const [start, setStart] = useState<XYPosition | null>(null);
const [end, setEnd] = useState<XYPosition | null>(null);
const { screenToFlowPosition, getViewport, setNodes } = useReactFlow();
function handlePointerUp() {
if (!start || !end) return;
const position = screenToFlowPosition(getPosition(start, end));
const dimension = getDimensions(start, end, getViewport().zoom);
setNodes((nodes) => [
...nodes,
{
id: crypto.randomUUID(),
type: 'rectangle',
position,
...dimension,
data: { color: getRandomColor() },
},
]);
}
}ポイント
onPointerDown/onPointerMove/onPointerUpでドラッグ操作を検出screenToFlowPosition()でスクリーン座標をフロー座標に変換する- 新しい矩形ノードには
crypto.randomUUID()で一意な ID を生成 getViewport().zoomで現在のズームレベルを考慮してノードサイズを計算する- 「Rectangle Mode」と「Selection Mode」を切り替えるボタンを提供
---
Freehand Draw
フリーハンドで描いた曲線がカスタムノードに変換されてフロー内でノードとして操作できる。
ポイント
- マウスイベントで描画パスを記録してビューポート全体にキャプチャ
perfect-freehandライブラリで生の座標を滑らかな曲線に変換- 描画完了後に SVG パスまたはカスタムノードとして永続化する
- スクリーン座標とフローキャンバス座標間の変換を処理する
- 描画はカスタムノードとして通常の React Flow スタイリングと操作が可能
- Pro example: フルソースコードは React Flow Pro サブスクリプションが必要
React Flow — Hooks
| Name | Description | Path |
|---|---|---|
useConnection | 進行中の接続操作の状態を取得 | ./useConnection.md |
useEdges | 現在のエッジ配列を取得 | ./useEdges.md |
useEdgesState | エッジの状態と変更ハンドラを管理(非推奨) | ./useEdgesState.md |
useHandleConnections | 特定ハンドルの接続情報を取得 | ./useHandleConnections.md |
useInternalNode | 内部ノードオブジェクト(寸法・位置含む)を取得 | ./useInternalNode.md |
useKeyPress | 特定キーの押下状態を監視 | ./useKeyPress.md |
useNodeConnections | 特定ノードの全接続情報を取得 | ./useNodeConnections.md |
useNodeId | カスタムノード内で自身のノード ID を取得 | ./useNodeId.md |
useNodes | 現在のノード配列を取得 | ./useNodes.md |
useNodesData | 指定ノードの data プロパティを取得 | ./useNodesData.md |
useNodesInitialized | 全ノードの寸法計算完了を検知 | ./useNodesInitialized.md |
useNodesState | ノードの状態と変更ハンドラを管理(非推奨) | ./useNodesState.md |
useOnSelectionChange | ノード/エッジの選択状態変更を監視 | ./useOnSelectionChange.md |
useOnViewportChange | ビューポートの変更(パン・ズーム)を監視 | ./useOnViewportChange.md |
useReactFlow | React Flow インスタンスのメソッドにアクセス | ./useReactFlow.md |
useStore | React Flow の内部 Zustand ストアから値を取得 | ./useStore.md |
useStoreApi | React Flow の内部 Zustand ストア API に直接アクセス | ./useStoreApi.md |
useUpdateNodeInternals | ノードの内部状態(ハンドル位置等)を手動更新 | ./useUpdateNodeInternals.md |
useViewport | 現在のビューポート(x, y, zoom)を取得 | ./useViewport.md |
useConnection
Returns the current connection state during an active connection interaction, or null for every property if no connection is in progress.
Signature
useConnection(
connectionSelector?: (connection: ConnectionState<InternalNode<NodeType>>) => SelectorReturn
): SelectorReturn | ConnectionStateParameters
| Name | Type | Description |
|---|---|---|
connectionSelector | (connection: ConnectionState<InternalNode<NodeType>>) => SelectorReturn | Optional selector to extract specific connection state data. Prevents unnecessary re-renders by subscribing only to the needed slice. |
Returns
SelectorReturn or ConnectionState — When a connection interaction is active, returns connection state. Returns null for every property when no connection is active.
使用例
import { useConnection } from '@xyflow/react';
export default function CustomHandle() {
const connection = useConnection();
return (
<div>
{connection
? `Incoming connection from ${connection.fromNode}`
: 'No incoming connections'}
</div>
);
}注意点
<ReactFlow>または<ReactFlowProvider>の子コンポーネント内でのみ使用可能- 接続インタラクションが非アクティブな場合、
null参照ではなく各プロパティがnullになる - ハンドルのカラー変化などの条件付き UI に利用するのが典型的なユースケース
connectionSelectorを活用することで不要な再レンダリングを防ぎパフォーマンスを向上できる
関連
- useHandleConnections.md
- useNodeConnections.md
useEdges
Returns an array of all current edges in the flow; re-renders the component whenever any edge changes.
Signature
useEdges<EdgeType extends Edge = Edge>(): EdgeType[]Parameters
なし
Returns
EdgeType[] — フロー内の全エッジの配列
使用例
import { useEdges } from '@xyflow/react';
export default function EdgeCounter() {
const edges = useEdges();
return <div>There are currently {edges.length} edges!</div>;
}カスタム型を使う場合:
const edges = useEdges<CustomEdgeType>();注意点
- パフォーマンス警告: エッジが少しでも変化するたびに再レンダリングが発生する。エッジの数だけ知りたいなど特定のスライスのみ必要な場合は
useStoreの利用を検討する <ReactFlow>または<ReactFlowProvider>の子コンポーネント内でのみ使用可能
関連
- useNodes.md
- useEdgesState.md
- useStore.md
useEdgesState
Controlled フローのプロトタイプ作成を容易にするフック。エッジの状態を ReactFlowInstance の外部で管理する。
Signature
useEdgesState<EdgeType extends Edge = Edge>(
initialEdges: EdgeType[]
): [
edges: EdgeType[],
setEdges: Dispatch<SetStateAction<EdgeType[]>>,
onEdgesChange: OnEdgesChange<EdgeType>
]Parameters
| Name | Type | Description |
|---|---|---|
initialEdges | EdgeType[] | フローの初期エッジ配列 |
Returns
3要素のタプル:
| Index | Name | Type | Description |
|---|---|---|---|
| 0 | edges | EdgeType[] | 現在のエッジ配列。<ReactFlow edges={edges} /> に渡す |
| 1 | setEdges | Dispatch<SetStateAction<EdgeType[]>> | React の useState と同様の状態セッター |
| 2 | onEdgesChange | OnEdgesChange<EdgeType> | エッジ変更ハンドラー。onEdgesChange プロパティに渡す |
使用例
import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react';
const initialNodes = [];
const initialEdges = [];
export default function Flow() {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
/>
);
}注意点
- プロトタイピングやドキュメント例用に設計されており、本番環境では Zustand などより高度な状態管理の利用を推奨
- カスタムエッジ型のジェネリクス引数に対応
関連
- useNodesState.md
- useEdges.md
useHandleConnections
Deprecated: useNodeConnections() に移行してください。特定のハンドルまたはハンドルタイプへの接続の配列を返す。
Signature
useHandleConnections(options: {
type: 'source' | 'target';
id?: string | null;
nodeId?: string;
onConnect?: (connections: Connection[]) => void;
onDisconnect?: (connections: Connection[]) => void;
}): HandleConnection[]Parameters
| Name | Type | Required | Description |
|---|---|---|---|
type | `'source' \ | 'target'` | Yes |
id | `string \ | null` | No |
nodeId | string | No | ノード ID(省略時は NodeIdContext から自動取得) |
onConnect | (connections: Connection[]) => void | No | 接続確立時に呼ばれるコールバック |
onDisconnect | (connections: Connection[]) => void | No | 接続削除時に呼ばれるコールバック |
Returns
HandleConnection[] — 該当ハンドルへの接続の配列
使用例
import { useHandleConnections } from '@xyflow/react';
export default function MyNode() {
const connections = useHandleConnections({
type: 'target',
id: 'my-handle',
});
return (
<div>There are currently {connections.length} incoming connections!</div>
);
}注意点
- 非推奨 (Deprecated): より高機能な
useNodeConnections()を使用すること - 接続が変化するたびにコンポーネントが再レンダリングされる
- 同種ハンドルが1つだけの場合は
idの指定は不要
関連
- useNodeConnections.md
- useConnection.md
useInternalNode
指定した ID のノードの内部表現を返す。ノードに変化があるたびに再レンダリングが発生する。
Signature
useInternalNode<NodeType extends Node = Node>(
id: string
): InternalNode<NodeType> | undefinedParameters
| Name | Type | Description |
|---|---|---|
id | string | 取得するノードの ID |
Returns
InternalNode<NodeType> | undefined — 指定ノードの内部表現。見つからない場合は undefined。
InternalNode には通常の Node プロパティに加え、internals.positionAbsolute などの内部計算値が含まれる。
使用例
import { useInternalNode } from '@xyflow/react';
export default function AbsolutePositionDisplay() {
const internalNode = useInternalNode('node-1');
if (!internalNode) return null;
const { x, y } = internalNode.internals.positionAbsolute;
return (
<div>
Absolute position: x={x}, y={y}
</div>
);
}注意点
- 選択・移動を含むあらゆるノード変化で再レンダリングが発生する
- 絶対座標など内部計算値へのアクセスに使用する(通常の座標は親ノード相対)
- カスタムノード型のジェネリクス引数に対応
関連
- useNodes.md
- useNodesData.md
React Flow — Troubleshooting
| Name | Description | Path |
|---|---|---|
| Common Errors | よくあるエラーメッセージとその解決方法 | ./common-errors.md |
| Remove Attribution | React Flow のアトリビューション表示の削除方法 | ./remove-attribution.md |
| Migrate to v12 | v11 から v12 へのマイグレーションガイド | ./migrate-to-v12.md |
| Migrate to v11 | v10 から v11 へのマイグレーションガイド | ./migrate-to-v11.md |
| Migrate to v10 | v9 から v10 へのマイグレーションガイド | ./migrate-to-v10.md |