SnapDOMGitHub★ 8K
Live demo

Live element

Hard parts

transform + shadow

Result

Nothing captured yet

Edit the title, then run. This uses the real library from unpkg.
Library Comparison

SnapDOM vs html2canvas

Compare DOM-to-canvas and SVG-based capture, then review output types, font handling and the options needed to migrate.

TL;DR

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.

Test element to be captured by both libraries.
SnapDOM
Waiting to start…
html2canvas
Waiting to start…

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

FeatureSnapDOMhtml2canvas
Pseudo-elements (::before / ::after)SupportedPasses tested fixture
Web fonts (@font-face)EmbeddedBest-effort
Shadow DOMOpen rootsPasses tested fixture
CSS variablesYesPartial
CSS counter() / counters()YesDepends on the case
Same-origin <iframe> captureYesLimited
Line-clamp / multi-line ellipsisYesDepends on the case
Plugin systemYesNo
Output formatsSVG · PNG · JPG · WebP · Canvas · BlobPNG, JPG (via canvas.toDataURL)
Runtime dependencies00
ApproachClone + foreignObject (browser renders)Re-implements CSS painter
LicenseMITMIT

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:

html2canvasSnapDOM
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