Open-source project
zumerlab/snapdom avatar
zumerlab/snapdom

SnapDOM: a browser capture engine that keeps the capture after the DOM moves on

High-performance engine for capturing, modifying, and converting DOM elements into any format.

8,168 stars306 forksJavaScriptMIT

At a glance

What is it?
SnapDOM turns a rendered element into a reusable capture with styles, fonts and images included, then exports it as PNG, JPG, WebP, SVG or canvas. It is a fit for in-page screenshot features; it is not a headless renderer.
Who is it for?
Adopt SnapDOM when the element you want to capture already exists in a browser page and you need several outputs from one capture, since the result object stays valid after the source element changes. Do not adopt it if your capture has to happen on a server or in a headless browser with no page context, because the README describes everything as running in the page with standard Web APIs.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What SnapDOM captures, and for whom

SnapDOM is a browser capture engine for web interfaces. The README describes it as capturing rendered DOM state as a reusable result, with styles, fonts and images included. That wording matters: the input is a live element in a rendered page, not an HTML string and not a URL.

The intended audience is frontend engineers who need to turn part of a page into an image or a canvas. The README lists shareable cards, charts, invoices and dashboards as the core cases, plus reuse of a capture as a WebGL texture, an overlay or a UI transition. A second group is served by plugins: saving a page fragment as self-contained HTML, giving an agent or a log a view of page content, and producing image-based PDFs, animated GIFs or browser-encoded video.

The scope boundary is the browser. Everything runs in the page using standard Web APIs, and the core has no dependencies. Nothing in the README describes a Node entry point or a server-side renderer, so a team that needs to screenshot at build time or in a queue worker is looking at the wrong tool.

The capture result is the core design decision

Most of the API surface follows from one choice: a capture is a value, not a one-shot conversion. Calling snapdom(element) returns a result, and the result keeps that capture even if the source element later changes. Call snapdom(card) again to capture its new state.

That is why the README separates one-step shortcuts from the two-step form. snapdom.toPng(element, options) captures and exports in one call, which is what you want for a single output. When you need several outputs, you capture once and export repeatedly from the same frozen state, so the PNG, the canvas and the blob cannot drift apart because something re-rendered between calls.

The export methods split by return type. toPng(), toJpg() and toWebp() return an HTMLImageElement; toSvg() returns an SVG-backed HTMLImageElement; toCanvas() returns an HTMLCanvasElement; toBlob() returns an SVG Blob unless a format was explicitly set on the capture or the export. toRaw() and the url property give you the capture's SVG data URL, and to(name, options?) dispatches to a core or plugin exporter by name. The README notes toJpeg() as an alias for toJpg() and says toImg() remains available but toSvg() is preferred for an SVG image.

There is a real distinction in the plugin set that the README states plainly: image, HTML and context exports use the captured state, while the GIF and video plugins record the live element over time. If you need an animation, the frozen-capture model does not apply to those two outputs.

Install and capture your first element

The core installs from npm as @zumer/snapdom. The README instructs you to install the core and, when you need them, the official plugins, using matching major versions. The package is ESM-only: package.json sets "type": "module" and the exports map points both the root and ./plugins at dist/snapdom.mjs. The README states there is no CommonJS build.

bash
npm i @zumer/snapdom@latest @zumer/snapdom-plugins@latest

With the package installed, importing snapdom and calling toPng on a selected element is the shortest path to a real result. The README gives this as the quick start, and the returned value is an image element you append yourself.

js
import { snapdom } from '@zumer/snapdom';

const card = document.querySelector('#card');
const image = await snapdom.toPng(card);
document.body.appendChild(image);

If you would rather not bundle it, the README also shows a script tag that exposes window.snapdom, served from unpkg, and an ES module import from esm.sh. Both examples load the latest published core and plugins.

For the multi-output case, capture first and export from the result. Note that download() takes a format and a filename, and that the capture options are passed to the snapdom() call, not to each export.

js
const result = await snapdom(card);

const image = await result.toPng();
const canvas = await result.toCanvas();
const blob = await result.toBlob({ format: 'png' });
await result.download({ format: 'jpg', filename: 'card' });

Sizing and content filtering are set at capture time. The README's example passes width, dpr, backgroundColor, exclude and excludeMode. Two behaviours are worth reading twice: width and height define output size and preserve the aspect ratio if only one is set, and scale applies only when neither is set, while dpr multiplies the pixel dimensions. The exclude option takes selectors or predicates, where true means exclude, and excludeMode defaults to 'hide', which keeps an invisible spacer; 'remove' takes the node out of the layout instead. A separate filter option uses true to keep a node and false to filter it out, with its own filterMode defaulting to 'hide' and described as an independent layout mode.

js
const result = await snapdom(card, {
  width: 800,
  dpr: 1,
  backgroundColor: '#ffffff',
  exclude: '.capture-ignore',
  excludeMode: 'remove'
});

If you are working against a local checkout rather than the published package, the README gives npm install, npm run compile and npm run site, and notes that the local site runs the local build while the public site's demos load the published package.

Where the browser-only model breaks down

The constraint is the same one that makes the library small: there is no page outside a page. SnapDOM captures rendered state, so anything that has not been laid out cannot be captured. A capture pipeline that runs in CI, in a queue worker, or in a scheduled job has no DOM to hand it, and the README documents no server-side entry point to work around that.

Two smaller edges are visible in the documented options. The scale and dpr interaction is easy to get wrong: if you set width or height, scale stops applying, and dpr still multiplies pixel dimensions on top. A team that sets both a width and a dpr expecting one to win will get a larger image than intended. The filter and exclude pair is also easy to conflate. They are separate options with separate modes, and excludeMode governs how excluded nodes are handled while filterMode governs nodes rejected by filter. Reading them as one setting produces captures with unexpected gaps or unexpected spacing.

A third limit is output-specific. toBlob() returns an SVG Blob unless a format was explicitly set on the capture or the export, which is a default that surprises people who expect a PNG blob from a method with no arguments. The README does not document rollback behaviour for the result object, so if you need to revert a capture there is nothing published to rely on.

SnapDOM against html2canvas and the dom-to-image family

The related searches around this project are dominated by comparisons, and the README's own topic list names html2canvas-alternative and html-to-image. The honest difference is architectural rather than a feature checklist.

SnapDOM's README describes captures as running in the page with standard Web APIs and no core dependencies, and it exposes the capture as an SVG data URL through toRaw() and the url property. html2canvas is widely used for the same job, turning a DOM subtree into a canvas, but the two differ in what they hand back: SnapDOM's result is a reusable capture object with multiple exporters attached, so one capture can produce a PNG, a canvas, a blob and a download without re-reading the DOM. If your code only ever needs one PNG from one element, that difference buys you little.

The dom-to-image family and modern-screenshot sit in the same space, and the same question applies to them: does your code need several outputs from one frozen state, or one output from a live element? SnapDOM's plugin layer is the other axis. HTML export with captured styles and fonts, context export for agents, image-based PDF, GIF and video are shipped as plugins rather than core, which keeps the core small but means a plugin-heavy setup carries more moving parts and a version-matching requirement between core and plugins.

Licence, releases and the cost of upgrading

SnapDOM is MIT licensed, which permits commercial use and modification under the terms of that licence. That is the extent of what the repository states; the licence text itself is the authority, not this summary.

The upgrade story is the part to plan for. The README documents v3.x.x and includes a migration guide comparing it with v2.x.x, and it says the v2 source and v2 documentation remain available. Releases are frequent: v3.0.0 on 2026-09-14, v3.1.0 on 2026-09-21, with v2.24.18 on 2026-09-11 before the major bump. A major version landing a week before the current release, with a migration guide as the only bridge, means the upgrade cost is real for anyone on 2.x.

Version matching is the other recurring cost. The README tells you to install the core and the official plugins with matching major versions, so a plugin cannot be upgraded independently of the core. The package is published as @zumer/snapdom with the plugins in the same workspace layout, and the exports map resolves both the root and ./plugins to the same dist/snapdom.mjs file, described as sharing the same runtime and plugin registry. The last push to the repository was on 2026-09-21, the same day as the v3.1.0 release.

Editorial conclusion

Adopt SnapDOM when the element you want to capture already exists in a browser page and you need several outputs from one capture, since the result object stays valid after the source element changes. Do not adopt it if your capture has to happen on a server or in a headless browser with no page context, because the README describes everything as running in the page with standard Web APIs. Before committing, check that your build can consume an ES module, since there is no CommonJS build, and read the v2 migration guide if you are upgrading from 2.x.

Frequently asked questions

Does SnapDOM work in Node.js or only in the browser?

The README describes SnapDOM as running in the page using standard Web APIs, and it documents no server-side entry point. Captures are taken from rendered DOM state, so there has to be a browser page holding the element.

Why does SnapDOM's toBlob() return an SVG blob instead of a PNG?

The README states that toBlob() returns an SVG Blob unless a format was explicitly set on the capture or on the export. To get a PNG blob, pass the format either when capturing or when exporting.

Can I use SnapDOM with an older CommonJS build setup?

No. package.json sets "type": "module" and the exports map points at dist/snapdom.mjs, and the README states there is no CommonJS build. You need an ES module aware bundler or the script tag build that exposes window.snapdom.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. zumerlab/snapdom on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zumerlab-snapdom.svg)](https://hysenlabs.com/projects/zumerlab-snapdom)