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
| Feature | SnapDOM | modern-screenshot |
|---|---|---|
| Rendering approach | Clone + foreignObject | Clone + foreignObject |
| Output formats | SVG · PNG · JPG · WebP · Canvas · Blob | PNG · JPEG · WebP · SVG · Canvas · Blob |
| Plugin system | Yes | No |
Web fonts (@font-face) | Embedded | Yes |
| Pseudo-elements | Yes | Yes |
| Shadow DOM | Yes | Partial |
| TypeScript-first API | Typed, JS core | Yes |
| Runtime dependencies | 0 | 0 |
| Maintained | Active | Active |
| License | MIT | MIT |
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-screenshot | SnapDOM |
|---|---|
| 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