Skip to content

Migrating from other JS Graphviz libraries

viz.js / @viz-js/viz, @hpcc-js/wasm (@hpcc-js/wasm-graphviz), and d3-graphviz all give JavaScript access to Graphviz by compiling the real C Graphviz to WebAssembly and calling into it. @knowvah/dot-engine is a from-scratch TypeScript port — the layout engines, parser, and SVG emitter are TypeScript source, not a compiled binary.

That difference is the headline, not a footnote:

WASM wrappers (viz.js / @hpcc-js/wasm / d3-graphviz)@knowvah/dot-engine
ImplementationReal C Graphviz, compiled to a .wasm binaryPure TypeScript port, no compiled artifact
Module initAsync — instantiate/await the WASM module before first useNone — import and call synchronously
BundleShip a .wasm asset (hundreds of KB–low MB) alongside JSJS only, tree-shakeable
DebuggingStep through a WASM blob (or C source, if you have it)Step through the real TypeScript with source maps
Threading modelSome builds run layout in a Web WorkerRuns on the calling thread, like any TS function
Output formatsWhatever the underlying C build was compiled with — typically the full Graphviz set, including raster/PDFSVG + the DOT/json/xdot/plain/imagemap text formats — see below

If your use case is "call a function, get SVG back, no async ceremony, no WASM asset to host" — that's what @knowvah/dot-engine is for. If your use case depends on raster or PDF output, see When to stay on WASM below.

API deltas

The three libraries have different shapes; the table below is the common migration case (approximate — verify against each library's own docs; see citations below each row).

LibraryTypical call@knowvah/dot-engine equivalent
@viz-js/viz (successor to viz.js)Viz.instance().then(viz => viz.renderSVGElement(dot)) — async, Viz.instance() resolves a PromiserenderSvg(dot, 'dot') — synchronous, no instance/init step
viz.js 2.x (legacy, new Viz())new Viz().renderString(dot) — returns a Promise<string>renderSvg(dot, 'dot') — synchronous
@hpcc-js/wasm-graphvizawait Graphviz.load() once, then graphviz.dot(dot) (sync after load)renderSvg(dot, engine) — no load/warm-up step at all
d3-graphvizd3.select(sel).graphviz().renderDot(dot) — binds output into the DOM, animates transitionsrenderSvg(dot, engine) returns an SVG string; you insert it into the DOM yourself (e.g. el.innerHTML = svg)

Every @knowvah/dot-engine call in the right column is synchronous — there is no module to await, because there's no WASM binary to instantiate. Drop any await/.then() wrapping a @knowvah/dot-engine call; it was never needed.

  • @viz-js/viz's Viz.instance() → Promise and renderSVGElement() method are documented at viz-js.com; confirmed via the project's published usage example as of writing.
  • viz.js 2.x's new Viz().renderString(dot) is the API documented for that (now-superseded) release line; if you're on a current install, check whether you're actually on @viz-js/viz.
  • @hpcc-js/wasm-graphviz's Graphviz.load() / graphviz.dot() pair is confirmed via the package's published usage example as of writing. The separate, older @hpcc-js/wasm package additionally exposed a graphviz.layout(dot, format, engine) call in past releases — check your installed version's own docs before relying on the exact signature.
  • d3-graphviz's .graphviz().renderDot(dot) chain, and that it is built on @hpcc-js/wasm internally, is confirmed via the project's published README as of writing.

renderDot's DOM binding is out of scope here

d3-graphviz does more than render SVG: it binds the result into a D3 selection, diffs re-renders, and animates transitions between layouts. @knowvah/dot-engine has no DOM opinion at all — renderSvg/render return a plain string. If you want d3-graphviz-style animated transitions between two layouts, that's logic you'd build on top of two renderSvg calls and your own DOM diffing (or keep using d3-graphviz for that specific feature — see below).

Getting layout data without parsing a string format

All three WASM libraries can be asked for Graphviz's own JSON or plain text formats, then you parse that string yourself to get node/edge coordinates. @knowvah/dot-engine skips the text round-trip: call getLayout(g) (after render) for a typed, JSON-serializable snapshot directly — no -Tjson/-Tplain string to parse.

ts
import { createGraph, render, getLayout } from '@knowvah/dot-engine';

const b = createGraph({ directed: true });
b.addNode('a');
b.addNode('b');
b.addEdge('a', 'b');

render(b.graph, 'svg');
const layout = getLayout(b.graph);
// layout.nodes  → [{ name: 'a', x, y, width, height }, ...]
// layout.edges  → [{ tail: 'a', head: 'b', points: [...] }]
// layout.bounds → { x, y, width, height }

See Read computed geometry for the full snapshot shape, units, and the yAxis option.

When to stay on WASM

Be honest with yourself about scope: @knowvah/dot-engine targets SVG plus the dot/xdot/json/plain/plain-ext/imap/cmapx text formats. It does not emit raster formats (PNG/JPEG/GIF/...) or PostScript/PDF/EPS — those are an intentional scope boundary, not a gap that's merely unfinished. See Known divergences for the exact non-goals list.

If your application needs -Tpng or -Tpdf output directly from the layout engine, the WASM-based libraries above still cover that case — because they're running the real C Graphviz, they support whatever output formats that build was compiled with. In that scenario, either keep using the WASM library for that one code path, or render to 'svg' with @knowvah/dot-engine and convert the SVG to a raster/PDF downstream with a separate tool.

See also