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, at the same zero-dependency footprint, so the extra capability comes for free. Both are MIT.
SnapDOM vs modern-screenshot, head to head
Both libraries export the same DOM element to PNG five times. Times are averaged, the faster one is highlighted. Run it on your machine. The gap is hardware- and content-dependent.
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 |
Feature support in other libraries evolves. For anything version-specific, check their docs for the latest. Or run the live benchmark above and compare the output yourself.
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.
What a minimal library leaves to you…
- Watermarks, filters, PDF and custom exports become code you write and maintain yourself.
- Shadow DOM and Web Component edge cases are yours to debug.
- Safari rendering quirks and complex
@font-facesetups need hand-rolled workarounds. - Fewer output formats means extra conversion plumbing in your app.
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.
Compare the output
Capture any DOM element to PNG with a single import. No build step, no dependencies.
Open the demo Install from npm