JavaScript/TypeScript Dependency Graph: 5 Ways to Generate One (Interactive HTML)

Raghav Chamadiya8 min read

javascript dependency graph · typescript dependency graph · html dependency graph · dependency graph tool · madge dependency graph · dependency-cruiser

On this page

To generate a JavaScript or TypeScript dependency graph, run npx madge --image graph.svg src/ for a quick SVG, or npx depcruise src -T dot-webpage -f graph.html for interactive HTML (both need Graphviz). Use npx nx graph for a package-level monorepo graph, esbuild's --metafile for what a bundle pulls in, and repowise for a browsable graph with history.

Tool (version on npm)One commandUnitOutputNeedsBest at
madge 8.0.0npx madge --extensions ts,tsx --image graph.svg src/FileText, JSON, DOT, SVG or PNGGraphviz for images; Node 18+Circular imports, a quick picture
dependency-cruiser 18.5.0npx depcruise src -T dot-webpage -f graph.htmlFile, or folder with archidot, mermaid, d2, json, csv, HTML matrix, interactive HTMLGraphviz for dot-webpage and SVG; Node 22+Rules in CI, focused views
Nx 23.2.1npx nx graph --file=graph.htmlProject and taskBrowser UI, static HTML, JSONNx in the repo (nx init)Package graph in a monorepo
esbuild 0.28.2npx esbuild src/index.ts --bundle --metafile=meta.json --outfile=out.jsFile reached from an entry pointJSON metafile, text with --analyzeAn entry point that bundlesWhat ships in the bundle
repowisePaste a GitHub URL, or repowise init then repowise serveFile, symbol, layerInteractive graph in the browser, JSON, Structurizr DSLNothing for a public repo; pip install repowise locallyTracing paths, packages vs imports, co-change

Scroll the table sideways to see every column.

1. madge: the quick graph and circular imports

madge reads imports with a resolver per file type and prints, draws or exports the result.

bash
# list each file's dependencies
npx madge --extensions ts,tsx --ts-config tsconfig.json src/

# circular imports only
npx madge --circular --extensions ts,tsx src/

# SVG image (needs Graphviz: brew install graphviz / apt-get install graphviz)
npx madge --extensions ts,tsx --image graph.svg src/

# raw data
npx madge --extensions ts,tsx --json src/ > graph.json
npx madge --extensions ts,tsx --dot src/ > graph.gv

--orphans lists files nothing imports and --leaves lists files that import nothing. Pass --ts-config so path aliases like @/components resolve; without it they show up as missing.

madge outputs text in the terminal, JSON, DOT, or an image whose format comes from the file extension, and it has no HTML output. Past a few hundred files the SVG gets too dense to read, so point it at one folder or use --circular --image to draw only the cycles. Its latest npm release is from August 2024, which is worth knowing before you build CI around it.

2. dependency-cruiser: interactive HTML and rules

dependency-cruiser does what madge does and adds rules you can enforce in CI. It has the widest choice of output formats of any tool here.

bash
npm install --save-dev dependency-cruiser
npx depcruise --init        # writes a config with rules like no-circular and no-orphans

# interactive HTML: hover a module to highlight what it imports and what imports it
npx depcruise src --include-only "^src" -T dot-webpage -f graph.html

# SVG
npx depcruise src --include-only "^src" -T dot | dot -T svg > graph.svg

# folder-level overview instead of every file
npx depcruise src -T archi | dot -T svg > overview.svg

# text formats that need no Graphviz
npx depcruise src -T mermaid -f graph.mmd
npx depcruise src -T json -f graph.json
npx depcruise src -T html -f matrix.html    # a dependency matrix, not a drawing

# rules as a CI gate
npx depcruise src --config

The flags that keep the graph readable on a real codebase are --focus (a module and its neighbours), --reaches (everything that depends on a module), --affected (only what changed since a git revision, plus what reaches it) and --collapse (summarise to a folder depth).

The dot-webpage reporter runs Graphviz and wraps the SVG in an HTML page with hover highlighting and click-through to source. The reporter used to be called x-dot-webpage, and the old name still works. Mermaid output renders directly in GitHub markdown. The current release needs Node 22 or newer.

3. Nx graph: the package view of a monorepo

Nx draws a graph of projects and tasks, and it does not show individual files. In an existing npm, yarn or pnpm workspace, run:

bash
npx nx@latest init          # adds Nx to the repo
npx nx graph                # opens the interactive graph in a browser
npx nx graph --focus=my-app # one project and everything connected to it
npx nx graph --affected     # highlight what your changes touch
npx nx graph --file=graph.html   # static site you can host or attach
npx nx graph --file=graph.json   # raw project graph

Nx works out dependencies by analysing source code, installed packages and TypeScript config, so an import between packages shows up even if a package.json forgot to declare it.

Nx gives you a browser UI with a button that saves the view as PNG, a static HTML site with its assets, or JSON. It is the right graph for "which packages depend on which" and the wrong one for "which file imports which". For choosing a monorepo tool by how it builds this graph, see migrating 50+ projects into a monorepo.

4. esbuild's metafile: what a bundle really pulls in

The other tools read every file in a folder. esbuild starts at an entry point and follows imports the way your build does, so its graph is exactly what ships.

bash
npx esbuild src/index.ts --bundle --metafile=meta.json --outfile=out.js

# text tree; verbose shows the import path that pulled each file in
npx esbuild src/index.ts --bundle --outfile=out.js --analyze=verbose

meta.json lists every input with its imports. Upload it to the esbuild bundle analyzer for a size breakdown, or turn it into a DOT graph with jq:

bash
( echo 'digraph deps {'
  jq -r '.inputs | to_entries[] | .key as $f | .value.imports[]
         | select(.external != true) | "  \"\($f)\" -> \"\(.path)\";"' meta.json
  echo '}' ) > deps.dot
dot -Tsvg deps.dot > deps.svg

esbuild writes JSON, plus a text report. It includes node_modules on purpose, because its job is to answer "why is this package in my bundle". If you only want the file list, TypeScript's tsc --explainFiles prints every file in the compile and the reason it was included, with no bundling.

5. repowise: a browsable graph with history

I build repowise, so read this section with that in mind. It parses imports and calls into a file and symbol graph, resolves tsconfig path aliases and npm, yarn and pnpm workspaces, and adds one thing the files never say: which files change together in git history.

For a public repo, paste the GitHub URL at repowise.dev and there is nothing to install. To run it locally on any repo:

bash
pip install repowise
cd your-repo
repowise init        # no API key needed for the graph
repowise serve       # web UI on localhost:3000

repowise gives you an interactive graph in the browser, a layered knowledge-graph.json in .repowise/, and repowise export --format structurizr for a C4 model. The same index answers coding agents over MCP (Model Context Protocol, the standard way agents call tools), for example "what depends on this file". It has two limits worth knowing: it has no CI rule engine like dependency-cruiser's, and it shows the whole repo, so it cannot give you one bundle's view the way esbuild does.

Worked example: react/react

React is a JavaScript monorepo with TypeScript and Rust alongside, so it exercises most of what these tools do. On react/react's architecture page, the index covers 2,764 source files and 15,233 symbols, with 64 files marked as entry points.

The default Map view draws 1,047 files and 3,633 dependencies, with 318 third-party modules hidden and 135 unlinked files left out so the picture stays readable. You can pick two files and trace the path between them, switch to communities (the clusters found in the graph, such as react-reconciler, react-dom-bindings and compiler/crates), or filter to hotspots. The Packages view joins 360 declared third-party packages, across 70 manifests, to the imports that use them.

madge or dependency-cruiser on the same repo would give you a precise file graph of one package at a time. Nx would give you the package graph. The repowise browser view is for the question in between, which is how a change in one package reaches another.

Which one should you use?

  • "Do I have circular imports?" Use madge --circular, or dependency-cruiser's no-circular rule in CI. Finding circular dependencies in a monorepo has recipes for TS, Python and Go.
  • "I need an HTML graph to share." Use dependency-cruiser dot-webpage, or nx graph --file=graph.html for packages.
  • "Which packages depend on this one?" Use Nx graph with --focus.
  • "Why is this in my bundle?" Use esbuild --analyze=verbose.
  • "How does this codebase fit together, and what changes with what?" Use repowise, or read building a dependency graph for any codebase for how such a graph is built.

If your services live in separate repos, an import graph stops at the repo edge. Cross-repo and cross-language dependency graphs covers what links them instead. For a broader comparison, see best dependency graph tools for monorepos.

How we measured

On 2026-10-06 we checked every flag above against each tool's current README or docs, and read each package's latest version and Node engine from the npm registry. We tested the jq conversion on a sample metafile. We did not time the tools against each other or run them on react/react. The react/react numbers come from repowise's hosted index (snapshot completed 2026-10-02), read from the live architecture page and the database the same day, and describe one commit.

FAQ

How do I make an HTML dependency graph?

Run `npx depcruise src --include-only "^src" -T dot-webpage -f graph.html` with Graphviz installed. It writes one HTML file with hover highlighting. For a package-level graph in a monorepo, `npx nx graph --file=graph.html` writes a static site.

What is the best dependency graph tool for JavaScript?

The answer depends on the question you have: madge is the fastest start, dependency-cruiser is the most configurable and enforces rules, Nx is best for packages in a monorepo, and esbuild shows what a bundle includes. Most teams end up using two, one for a picture and one for CI.

How do I generate a dependency graph with madge?

Install Graphviz, then run `npx madge --extensions ts,tsx --image graph.svg src/`. Add `--ts-config tsconfig.json` if you use path aliases. Use `--json` or `--dot` for data you can process yourself.

Is there a dependency chart tool that needs no install?

For a public GitHub repo, repowise builds the graph from the URL and shows it in the browser. For local code, Mermaid output from `depcruise -T mermaid` renders in any GitHub markdown file, so the chart lives next to the code.

How do I see which npm packages depend on a package?

For installed packages, `npm explain <name>` shows why a package is in `node_modules`, and `npm ls --all` prints the whole tree. For packages inside your own monorepo, `nx graph --focus=<project>` or dependency-cruiser's `--reaches` answers it from the code.