@dagrejs/dagre and graphlib are foundational libraries for working with graph theory in JavaScript, but they solve distinct problems. graphlib is a pure data structure library that provides the building blocks to create, traverse, and manipulate graphs (nodes and edges) in memory. It handles the 'what' of your graph. @dagrejs/dagre, on the other hand, is a layout engine specifically designed for Directed Acyclic Graphs (DAGs). It takes a graph structure (often built with graphlib) and calculates the x and y coordinates for every node to create a readable, hierarchical visualization. It handles the 'where' of your graph. While graphlib is often a dependency of dagre, using graphlib alone gives you no visual positioning, and using dagre without understanding the underlying graph structure limits your ability to customize the input.
When building applications that involve networks, dependencies, or flows, developers often face a choice between managing the data structure of the graph and calculating its visual presentation. @dagrejs/dagre and graphlib address these two separate layers of the problem. While they are historically linked—dagre uses graphlib internally—they serve different purposes in a modern frontend architecture. Let's dig into how they differ and when to use each.
@dagrejs/dagre is a layout engine. Its sole job is to take a graph and assign x and y coordinates to every node so that edges flow logically (usually top-to-bottom or left-to-right). It does not store your data long-term; it processes it to produce a layout.
import dagre from '@dagrejs/dagre';
const g = new dagre.graphlib.Graph();
g.setGraph({ rankdir: 'TB' }); // Top-to-Bottom
// Define nodes and edges
g.setNode('A', { width: 50, height: 50 });
g.setNode('B', { width: 50, height: 50 });
g.setEdge('A', 'B');
// Calculate layout
dagre.layout(g);
// Result: Nodes now have x, y coordinates
g.node('A').x; // e.g., 100
g.node('A').y; // e.g., 50
graphlib is a data structure library. It provides the classes to create nodes, edges, and subgraphs, and offers algorithms to query them (like finding all predecessors or checking connectivity). It does not calculate visual positions.
import { Graph, alg } from 'graphlib';
const g = new Graph();
g.setNode('A');
g.setNode('B');
g.setEdge('A', 'B');
// Query structure, not position
const successors = alg.successors(g, 'A'); // Returns ['B']
const hasCycle = alg.findCycle(g); // Returns undefined if no cycle
// Note: g.node('A') has no x/y properties here
This is the most critical technical constraint. @dagrejs/dagre only works on Directed Acyclic Graphs (DAGs). If your data contains a cycle (A → B → A), the layout algorithm will fail or throw an error because it cannot establish a hierarchical rank.
// dagre: Will fail or produce undefined behavior with cycles
const g = new dagre.graphlib.Graph();
g.setEdge('A', 'B');
g.setEdge('B', 'A'); // Creates a cycle
try {
dagre.layout(g);
// In many versions, this throws or results in infinite loops/errors
} catch (e) {
console.error("Layout failed: Graph contains a cycle");
}
graphlib handles cycles natively. Since it is just a data model, you can represent any graph topology. It even includes specific algorithms to detect and find those cycles.
// graphlib: Handles cycles perfectly
const g = new Graph();
g.setEdge('A', 'B');
g.setEdge('B', 'A');
const cycle = alg.findCycle(g);
console.log(cycle); // Outputs the cycle path, e.g., ['A', 'B', 'A']
@dagrejs/dagre exposes configuration focused on visual aesthetics. You tune the rank direction, alignment, and spacing between layers. This is where you control how the final diagram looks.
// dagre: Visual configuration
const g = new dagre.graphlib.Graph();
g.setGraph({
rankdir: 'LR', // Left-to-Right layout
align: 'UL', // Align nodes to Upper-Left
nodesep: 50, // Pixels between nodes in same rank
ranksep: 100 // Pixels between ranks
});
dagre.layout(g);
graphlib exposes configuration for algorithmic operations. You don't set "spacing" here; instead, you pass options to specific algorithms like shortest path calculations (e.g., using Dijkstra's algorithm with custom weight functions).
// graphlib: Algorithmic configuration
const g = new Graph();
g.setEdge('A', 'B', { weight: 5 });
g.setEdge('B', 'C', { weight: 2 });
// Find shortest path using custom weight accessor
const path = alg.dijkstra(g, 'A', (edge) => edge.weight);
console.log(path['C'].distance); // 7
You need to let users drag nodes, but when they hit "Auto-Layout", the nodes should snap into a clean hierarchy.
graphlib (via dagre.graphlib) to manage the state, then run dagre to compute positions, and finally apply those coordinates to your DOM elements.import dagre from '@dagrejs/dagre';
function autoLayout(nodes, edges) {
const g = new dagre.graphlib.Graph();
g.setGraph({ rankdir: 'TB' });
// Load data into graphlib structure
nodes.forEach(n => g.setNode(n.id, { width: n.w, height: n.h }));
edges.forEach(e => g.setEdge(e.source, e.target));
// Compute positions
dagre.layout(g);
// Map back to your application state
return nodes.map(n => ({
...n,
x: g.node(n.id).x,
y: g.node(n.id).y
}));
}
You are writing a tool to determine the order in which to compile files. You need to detect circular dependencies before compiling.
graphlib alone.dagre adds unnecessary weight and constraints.import { Graph, alg } from 'graphlib';
function getCompileOrder(files) {
const g = new Graph();
files.forEach(f => g.setNode(f.name));
files.forEach(f => f.deps.forEach(d => g.setEdge(d, f.name)));
const cycle = alg.findCycle(g);
if (cycle) {
throw new Error(`Circular dependency detected: ${cycle.join(' -> ')}`);
}
return alg.topsort(g);
}
| Feature | @dagrejs/dagre | graphlib |
|---|---|---|
| Primary Goal | Calculate x/y coordinates for visualization | Store and query graph relationships |
| Graph Type | Directed Acyclic Graphs (DAGs) only | Any (Directed, Undirected, Cyclic) |
| Output | Graph with x, y, rank on nodes | Graph structure, paths, traversal lists |
| Key Algorithms | Hierarchical layout, rank assignment | Dijkstra, BFS, DFS, Topological Sort, Cycle Detection |
| Visual Config | Rank direction, node spacing, alignment | None (Pure data) |
| Dependencies | Depends on graphlib | Zero dependencies |
Think of graphlib as the database for your relationships. It knows who is connected to whom, how heavy the connection is, and whether a loop exists. It is the logic layer.
Think of @dagrejs/dagre as the artist. It takes that data and decides where to place each item on the canvas so humans can understand the flow. It is the presentation layer.
Final Thought: In most visualization projects, you will actually use @dagrejs/dagre, which bundles graphlib internally for its data model. However, if you are doing pure logic work (like dependency resolution or network analysis) without rendering a diagram, skip dagre entirely and reach for graphlib to keep your bundle lean and your constraints flexible.
Choose @dagrejs/dagre when your primary goal is to automatically arrange nodes for visualization, specifically for flowcharts, dependency trees, or state machines. It is the right tool if you need to convert a logical graph into screen coordinates (x, y) without manually calculating positions. Be aware that it strictly requires the graph to be a Directed Acyclic Graph (DAG); if your data contains cycles, you must break them or use a different layout engine. Use this when building diagramming tools, workflow editors, or any UI where hierarchical clarity is essential.
Choose graphlib when you need to model complex relationships, perform algorithmic analysis (like finding shortest paths, detecting cycles, or traversing components), or manage graph state in memory without any visual rendering concerns. It is ideal for backend logic, build system dependency resolution, or as the data backbone for a custom visualization engine where you intend to write your own layout logic. If you only need to store connections and query relationships, and do not need automatic x/y positioning, this is the lightweight, focused choice.
Dagre is a JavaScript library that makes it easy to lay out directed graphs on the client-side.
For more details, including examples and configuration options, please see our wiki.
There are 2 versions on NPM, but only the one in the DagreJs org is receiving updates right now.
dagre is licensed under the terms of the MIT License. See the LICENSE file for details.