SnapDOM vs html2canvas
Compare DOM-to-canvas and SVG-based capture, then review output types, font handling and the options needed to migrate.
html2canvas normally paints the captured DOM onto Canvas with its JavaScript renderer. SnapDOM renders a styled clone through SVG, then exports images or Canvas as needed. SnapDOM also exposes a plugin lifecycle. Both are MIT-licensed and run in the browser.
SnapDOM vs html2canvas, head to head
Both libraries capture the same DOM element 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
html2canvas reads the DOM and paints elements onto a <canvas>. Its default renderer implements CSS support itself, so its supported properties and output can differ from the browser's page rendering.
SnapDOM clones the DOM, inlines styles and accessible fonts and images, and wraps the clone in an SVG <foreignObject>. It handles pseudo-elements, CSS variables, counters and open Shadow DOM, subject to browser rendering and resource-access limits.
Feature matrix
| Feature | SnapDOM | html2canvas |
|---|---|---|
Pseudo-elements (::before / ::after) | Supported | Passes tested fixture |
Web fonts (@font-face) | Embedded | Best-effort |
| Shadow DOM | Open roots | Passes tested fixture |
| CSS variables | Yes | Partial |
CSS counter() / counters() | Yes | Depends on the case |
Same-origin <iframe> capture | Yes | Limited |
| Line-clamp / multi-line ellipsis | Yes | Depends on the case |
| Plugin system | Yes | No |
| Output formats | SVG · PNG · JPG · WebP · Canvas · Blob | PNG, JPG (via canvas.toDataURL) |
| Runtime dependencies | 0 | 0 |
| Approach | Clone + foreignObject (browser renders) | Re-implements CSS painter |
| License | MIT | MIT |
Support varies by fixture and rendering mode; test your own UI. See the live benchmark for its pixel fixtures and loaded versions.
Run the live benchmark suite locally with npm run test:benchmark to reproduce the numbers on your hardware — performance varies a lot depending on element complexity, fonts and image content.
When to use which
Choose SnapDOM when…
- You use modern CSS: variables,
::before/::after, gradients, transforms, line-clamp. - Your UI uses Web Components or Shadow DOM.
- You need to embed custom fonts in the output.
- You need plugins for transformations or exports such as HTML, structured context, maps, ASCII or PDF images.
- You care about bundle size and zero dependencies.
Keep html2canvas when…
- Your existing captures meet your requirements and migration would add work without a measured benefit.
Compare representative output before changing libraries. Check the issue tracker for known problems affecting your content, then measure both implementations.
Same task, side by side
Capturing #card as a PNG and inserting the resulting image into the page:
html2canvas
import html2canvas from 'html2canvas'; const canvas = await html2canvas(document.querySelector('#card')); const img = new Image(); img.src = canvas.toDataURL('image/png'); document.body.appendChild(img);
SnapDOM
import { snapdom } from '@zumer/snapdom'; const img = await snapdom.toPng(document.querySelector('#card')); document.body.appendChild(img);
SnapDOM returns the HTMLImageElement ready to be appended — no manual toDataURL call. If you do need a canvas, call snapdom.toCanvas(el) instead.
Migration notes
Common operations have equivalents, but image return types and option meanings differ. These examples preserve the requested output format:
| html2canvas | SnapDOM |
|---|---|
| html2canvas(el) | snapdom.toCanvas(el) |
| canvas.toDataURL('image/png') | (await snapdom.toPng(el)).src |
| canvas.toDataURL('image/jpeg', 0.9) | (await snapdom.toJpg(el, { quality: 0.9 })).src |
| canvas.toBlob(cb, 'image/png') | await snapdom.toBlob(el, { format: 'png' }) |
| { scale: 2 } | { dpr: 2 } |
| { backgroundColor: '#fff' } | { backgroundColor: '#fff' } |
| { ignoreElements: skip } | { exclude: skip } |
| — (not supported) | { embedFonts: true } |
For a Canvas consumer, start with this change and compare the output:
// Before import html2canvas from 'html2canvas'; const canvas = await html2canvas(el, { scale: 2 }); // After import { snapdom } from '@zumer/snapdom'; const canvas = await snapdom.toCanvas(el, { dpr: 2 });
Frequently asked questions
Why does html2canvas not render my Shadow DOM?
Shadow DOM support depends on the capture implementation. SnapDOM walks accessible open Shadow roots, copies resolved styles and renders the clone through SVG <foreignObject>. Closed roots are not accessible through the standard DOM API.
html2canvas doesn't render web fonts correctly, what do I use?
First check that your fonts load before capture. SnapDOM v3.x.x uses embedFonts: 'auto' by default to embed used web fonts in the SVG. The font files must be accessible to the page; test the resulting image in your target browser.
html2canvas is slow on large DOMs, is there a faster alternative?
SnapDOM uses a different rendering path and can reuse eligible captures in v3. The gain depends on the DOM, assets and capture pattern. Use the live benchmark above and compare your own elements before choosing.
Is html2canvas still maintained in 2026?
This page compares html2canvas 1.4.1. For current maintenance status, check its releases and issue tracker. A release date alone does not establish whether a library meets your capture requirements.
Ready to switch?
Drop SnapDOM into your project, capture your first element, and decide for yourself.
Open the demo Installation