Library Comparison
zumerlab/snapdom

SnapDOM vs modern-screenshot

Two modern libraries, the same core idea: clone the DOM and let the browser render it inside an SVG <foreignObject>. Here's where they actually differ — and how to move between them.

Feature matrix Migration map
TL;DR

Both are actively maintained, fast, zero-dependency, and use the same foreignObject rendering approach — so basic captures look near-identical. modern-screenshot is minimal and TypeScript-first. SnapDOM layers on a plugin system, a broad output API (SVG, PNG, JPG, WebP, Canvas, Blob) and extra fidelity work for tricky CSS. Pick modern-screenshot for a lean dependency; pick SnapDOM when you want extensibility and the widest fidelity net. Both are MIT.

Same approach, different scope

modern-screenshot and SnapDOM belong to the same family as html-to-image: instead of re-implementing the CSS painter the way html2canvas does, they deep-clone the DOM, inline styles/fonts/images, and wrap the clone in an SVG <foreignObject> so the browser performs the actual render. That shared foundation is why both produce high-fidelity output and run quickly.

The differences are about scope, not approach. modern-screenshot keeps a tight, TypeScript-first surface focused on doing the capture well. SnapDOM aims wider: an extensible plugin pipeline, a larger export API, and a body of workarounds for edge cases (Safari quirks, font embedding, a clone-in-document measurement pass) that show up on complex real-world UIs.

Feature matrix

FeatureSnapDOMmodern-screenshot
Rendering approachClone + foreignObjectClone + foreignObject
Output formatsSVG · PNG · JPG · WebP · Canvas · BlobPNG · JPEG · WebP · SVG · Canvas · Blob
Plugin systemYesNo
Web fonts (@font-face)EmbeddedYes
Pseudo-elementsYesYes
Shadow DOMYesPartial
TypeScript-first APITyped, JS coreYes
Runtime dependencies00
MaintainedActiveActive
LicenseMITMIT

This table reflects emphasis, not a winner-takes-all — modern-screenshot is a capable, well-built library. For anything version-specific (its Shadow DOM and format support evolve), check its docs for the latest.

When to use which

Choose SnapDOM when…

  • You want to extend capture with plugins (watermarks, filters, custom exporters, PDF…).
  • Your UI uses Shadow DOM / Web Components and you need them captured reliably.
  • You hit edge cases (Safari rendering, complex @font-face, counters, line-clamp) and want them already handled.
  • You want one API spanning SVG, PNG, JPG, WebP, Canvas and Blob.

Choose modern-screenshot when…

  • You want the leanest possible dependency and a TypeScript-first API.
  • Your captures are straightforward and you don't need a plugin pipeline.
  • You prefer a minimal surface area over breadth of features.

It's a solid, maintained library — this isn't a "don't use it" verdict, just a scope trade-off.

Same task, side by side

Capturing #card as a PNG and inserting the resulting image into the page:

modern-screenshot

import { domToPng } from 'modern-screenshot';

const dataUrl = await domToPng(document.querySelector('#card'));
const img = new Image();
img.src = dataUrl;
document.body.appendChild(img);

SnapDOM

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

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

modern-screenshot's domToPng resolves to a data-URL string; SnapDOM's toPng resolves to a ready-to-append HTMLImageElement. If you want the raw data URL from SnapDOM instead, use snapdom.toRaw(el).

Migration map

The method names map cleanly. The main behavioural difference: SnapDOM's to* helpers return DOM-ready objects (an Image, Canvas or Blob) rather than data-URL strings.

modern-screenshotSnapDOM
domToPng(node)snapdom.toPng(node)
domToJpeg(node)snapdom.toJpg(node)
domToWebp(node)snapdom.toWebp(node)
domToSvg(node)snapdom.toSvg(node)
domToCanvas(node)snapdom.toCanvas(node)
domToBlob(node)snapdom.toBlob(node)

Frequently asked questions

Is SnapDOM or modern-screenshot faster?

They're in the same performance class because both delegate rendering to the browser via foreignObject rather than repainting in JavaScript. Real-world differences come down to element complexity, fonts and images — benchmark both on your own content rather than trusting a single headline number.

What does SnapDOM offer that modern-screenshot doesn't?

A plugin system for extending the capture pipeline, a broad export API (SVG, PNG, JPG, WebP, Canvas, Blob), reliable Shadow DOM capture, and a set of workarounds for tricky cases like Safari rendering and font embedding. modern-screenshot intentionally keeps a smaller surface area.

Can I migrate from modern-screenshot without rewriting everything?

Mostly yes — the method names map almost 1:1 (see the migration map above). The one adjustment is that SnapDOM returns DOM-ready objects (Image/Canvas/Blob) instead of data-URL strings; use snapdom.toRaw(el) if you specifically need a data URL.

Are both libraries free for commercial use?

Yes. Both SnapDOM and modern-screenshot are MIT-licensed and run entirely client-side, so there's no server cost or per-render fee.

Try SnapDOM in 30 seconds

Capture any DOM element to PNG with a single import. No build step, no dependencies.

Open the demo Install from npm