html-to-image is a popular, well-maintained fork of dom-to-image with TypeScript support. SnapDOM takes the same idea further with a plugin pipeline, native Shadow DOM walking, and out-of-the-box support for CSS counters, line-clamp and CSS variables.
SnapDOM vs html-to-image, 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.
A quick overview
html-to-image is the most popular community fork of dom-to-image. It modernised the codebase, added TypeScript, and improved fidelity for many edge cases. The API is small and predictable.
SnapDOM is a from-scratch alternative. The capture pipeline is broken into lifecycle hooks (beforeClone, afterClone, beforeRender, afterRender, beforeExport, afterExport), which is what enables its plugin system. Plugins ship for things like CSS filters, color tints, timestamps, ASCII export, PDF export and more.
Feature matrix
| Feature | SnapDOM | html-to-image |
|---|---|---|
| Active maintenance | Yes | Yes |
Pseudo-elements (::before / ::after) | Full | Yes |
Web fonts (@font-face) | Embedded + local | Embedded |
| Shadow DOM | Native | Manual |
| CSS variables | Yes | Yes |
CSS counter() / counters() | Yes | No |
| Line-clamp / multi-line ellipsis | Yes | No |
| Plugin system | Yes (8+ official) | No |
| Output formats | SVG · PNG · JPG · WebP · Canvas · Blob | SVG · PNG · JPG · Canvas · Blob |
| PDF export | Plugin | No |
| TypeScript types | Bundled | Bundled |
| License | MIT | MIT |
When to use which
Choose SnapDOM when…
- You use Web Components or Shadow DOM heavily.
- You need CSS counters or line-clamp ellipsis in the captured output.
- You want to add filters, watermarks, ASCII or PDF export without forking the library.
- You need fine-grained font control (
embedFonts,localFonts,excludeFonts).
What you give up with html-to-image…
- No plugin pipeline: watermarks, filters, PDF or ASCII exports are code you write yourself.
- Shadow DOM needs manual handling; CSS counters and line-clamp aren't captured.
- No WebP output, and font control stops at a single embed option.
Same task, side by side
Capturing #card as a PNG and inserting the resulting image into the page:
html-to-image
import { toPng } from 'html-to-image'; const dataUrl = await toPng(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);
Migration snippet
| html-to-image | SnapDOM |
|---|---|
| toPng(el) | snapdom.toPng(el) |
| toJpeg(el, { quality: 0.9 }) | snapdom.toJpg(el, { quality: 0.9 }) |
| toBlob(el) | snapdom.toBlob(el) |
| toCanvas(el) | snapdom.toCanvas(el) |
| toSvg(el) | (await snapdom(el)).toSvg() |
| { backgroundColor: '#fff' } | { backgroundColor: '#fff' } |
| { pixelRatio: 2 } | { dpr: 2 } |
| { filter: node => … } | { filter: node => … } |
| { fontEmbedCSS } | { embedFonts: true } |
| not supported | { plugins: [...] } |
// Before import { toPng } from 'html-to-image'; const dataUrl = await toPng(el, { pixelRatio: 2 }); // After import { snapdom } from '@zumer/snapdom'; const img = await snapdom.toPng(el, { dpr: 2 });
Frequently asked questions
What's actually different between html-to-image and SnapDOM?
Both clone the DOM and render via foreignObject. SnapDOM adds a plugin system with lifecycle hooks, native Shadow DOM walking, CSS counter resolution, line-clamp support, and ships first-party plugins for filters, color tint, timestamp watermark, ASCII export, PDF export and replace-text.
Does SnapDOM render web fonts better than html-to-image?
Both libraries can embed @font-face declarations into the SVG output. SnapDOM exposes embedFonts as a one-line opt-in and additionally supports localFonts and excludeFonts for fine-grained control over which fonts are embedded.
Can I add a watermark or filter without forking the library?
Yes. That's exactly what the SnapDOM plugin system is for. Use the colorTint, filter or timestampOverlay plugins, or write your own with two callbacks (afterClone / beforeExport). Browse the plugins page for the full list.
How do I export to PDF?
Use the pdfImage plugin. It adds a toPdfImage() method to the result object. See the html-to-png how-to for the same pattern with images.
Compare the output
Run both libraries against the same element and compare the output before changing the integration.
Open the demo Install from npm