Dagre Tree
This example shows how you can integrate dagre.js with React Flow to create simple tree layouts. Good alternatives to dagre are d3-hierarchy or elkjs if you are looking for a more advanced layouting library.
import React, { useCallback } from 'react';
import {
Background,
ReactFlow,
addEdge,
ConnectionLineType,
Panel,
useNodesState,
useEdgesState,
} from '@xyflow/react';
import dagre from '@dagrejs/dagre';
import '@xyflow/react/dist/style.css';
import { initialNodes, initialEdges } from './initialElements';
const dagreGraph = new dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
const nodeWidth = 172;
const nodeHeight = 36;
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);
const newNode = {
...node,
targetPosition: isHorizontal ? 'left' : 'top',
sourcePosition: isHorizontal ? 'right' : 'bottom',
// We are shifting the dagre node position (anchor=center center) to the top left
// so it matches the React Flow node anchor point (top left).
position: {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - nodeHeight / 2,
},
};
return newNode;
});
return { nodes: newNodes, edges };
};
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
initialNodes,
initialEdges,
);
const Flow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges);
const onConnect = useCallback(
(params) =>
setEdges((eds) =>
addEdge(
{ ...params, type: ConnectionLineType.SmoothStep, animated: true },
eds,
),
),
[],
);
const onLayout = useCallback(
(direction) => {
const { nodes: layoutedNodes, edges: layoutedEdges } =
getLayoutedElements(nodes, edges, direction);
setNodes([...layoutedNodes]);
setEdges([...layoutedEdges]);
},
[nodes, edges],
);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
connectionLineType={ConnectionLineType.SmoothStep}
fitView
style={{ backgroundColor: "#F7F9FB" }}
>
<Panel position="top-right">
<button onClick={() => onLayout('TB')}>vertical layout</button>
<button onClick={() => onLayout('LR')}>horizontal layout</button>
</Panel>
<Background />
</ReactFlow>
);
};
export function App() {
return <Flow />;
}
This example is a demonstration of static layouting. If the nodes or edges in the graph change, the layout won’t recalculate! It is possible to do dynamic layouting with dagre (and other libraries), though: see the auto layout pro example for an example of this.