Dagre Tree
This example shows how you can integrate dagre.js with Svelte 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.
<script lang="ts">
import dagre from '@dagrejs/dagre';
import {
SvelteFlow,
Background,
Position,
ConnectionLineType,
Panel,
type Node,
type Edge,
} from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css';
import { initialNodes, initialEdges } from './nodes-and-edges';
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
const nodeWidth = 172;
const nodeHeight = 36;
function getLayoutedElements(nodes: Node[], edges: Edge[], 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 layoutedNodes = nodes.map((node) => {
const nodeWithPosition = dagreGraph.node(node.id);
node.targetPosition = isHorizontal ? Position.Left : Position.Top;
node.sourcePosition = isHorizontal ? Position.Right : Position.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).
return {
...node,
position: {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - nodeHeight / 2,
},
};
});
return { nodes: layoutedNodes, edges };
}
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
initialNodes,
initialEdges,
);
let nodes = $state.raw<Node[]>(layoutedNodes);
let edges = $state.raw<Edge[]>(layoutedEdges);
function onLayout(direction: string) {
const layoutedElements = getLayoutedElements(nodes, edges, direction);
nodes = layoutedElements.nodes;
edges = layoutedElements.edges;
}
</script>
<div style="height:100vh;">
<SvelteFlow
bind:nodes
bind:edges
fitView
connectionLineType={ConnectionLineType.SmoothStep}
defaultEdgeOptions={{ type: 'smoothstep', animated: true }}
>
<Panel position="top-right">
<button onclick={() => onLayout('TB')}>vertical layout</button>
<button onclick={() => onLayout('LR')}>horizontal layout</button>
</Panel>
<Background />
</SvelteFlow>
</div>
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), but we don’t have an example for that right now!