@dagrejs/dagre vs graphlib
Graph Layout Algorithms vs. Data Structures in JavaScript
@dagrejs/dagregraphlibSimilar Packages:

Graph Layout Algorithms vs. Data Structures in JavaScript

@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.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
@dagrejs/dagre3,530,6365,7661.41 MB17212 days agoMIT
graphlib01,740-297 years agoMIT

Graph Layout vs. Graph Data: @dagrejs/dagre vs. graphlib

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.

🏗️ Core Purpose: Calculation vs. Storage

@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

🔄 Handling Cycles: Strict DAGs vs. General Graphs

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']

⚙️ Configuration: Visual Tuning vs. Algorithmic Control

@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

🎨 Real-World Integration Patterns

Scenario 1: Building a Flowchart Editor

You need to let users drag nodes, but when they hit "Auto-Layout", the nodes should snap into a clean hierarchy.

  • ✅ Best choice: Combine both.
  • Why? Use 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
  }));
}

Scenario 2: Build Dependency Resolver

You are writing a tool to determine the order in which to compile files. You need to detect circular dependencies before compiling.

  • ✅ Best choice: graphlib alone.
  • Why? You don't need x/y coordinates. You need to detect cycles and perform a topological sort. 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);
}

📌 Summary Table

Feature@dagrejs/dagregraphlib
Primary GoalCalculate x/y coordinates for visualizationStore and query graph relationships
Graph TypeDirected Acyclic Graphs (DAGs) onlyAny (Directed, Undirected, Cyclic)
OutputGraph with x, y, rank on nodesGraph structure, paths, traversal lists
Key AlgorithmsHierarchical layout, rank assignmentDijkstra, BFS, DFS, Topological Sort, Cycle Detection
Visual ConfigRank direction, node spacing, alignmentNone (Pure data)
DependenciesDepends on graphlibZero dependencies

💡 The Big Picture

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.

How to Choose: @dagrejs/dagre vs graphlib

  • @dagrejs/dagre:

    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.

  • graphlib:

    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.

README for @dagrejs/dagre

dagre - Graph layout for JavaScript

Build Status npm

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.

License

dagre is licensed under the terms of the MIT License. See the LICENSE file for details.