npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zumer/snapdom

v2.22.0

Published

Fast, modern alternative to html2canvas — captures HTML elements to images (PNG, JPG, SVG, WebP) with exceptional speed and accuracy.

Readme

SnapDOM

SnapDOM is a next-generation DOM Capture Engine — the fast, modern alternative to html2canvas, dom-to-image, and html-to-image.
It converts any DOM subtree into a self-contained representation that can be exported to SVG, PNG, JPG, WebP, Canvas, Blob, or any custom format through plugins — ultra-fast, modular, extensible, and dependency-free.

📖 Documentation, guides & live demos → snapdom.dev

Features

Full DOM capture with embedded styles, pseudo-elements and fonts; export to SVG, PNG, JPG, WebP, canvas or Blob — ultra fast, dependency-free, and 100% based on standard Web APIs.

👉 See the complete technical feature list in FEATURES.md.

Website & Live Demos

https://snapdom.dev

Quick Start

Capture any DOM element to PNG in one line:

import { snapdom } from '@zumer/snapdom';

const img = await snapdom.toPng(document.querySelector('#card'));
document.body.appendChild(img);

Reusable capture (one clone, multiple exports):

const result = await snapdom(document.querySelector('#card'));
await result.toPng();      // → HTMLImageElement
await result.toSvg();      // → SVG as Image
await result.download({ format: 'jpg', filename: 'card.jpg' });

Table of Contents

Installation

NPM / Yarn (stable)

npm i @zumer/snapdom
yarn add @zumer/snapdom

NPM / Yarn (dev builds)

For early access to new features and fixes:

npm i @zumer/snapdom@dev
yarn add @zumer/snapdom@dev

⚠️ The @dev tag usually includes improvements before they reach production, but may be less stable.

CDN (stable)

<!-- Minified build -->
<script src="https://unpkg.com/@zumer/snapdom/dist/snapdom.js"></script>

<!-- Minified ES Module build -->
<script type="module">
  import { snapdom } from "https://unpkg.com/@zumer/snapdom/dist/snapdom.mjs";
</script>

CDN (dev builds)

<!-- Minified build (dev) -->
<script src="https://unpkg.com/@zumer/snapdom@dev/dist/snapdom.js"></script>

<!-- Minified ES Module build (dev) -->
<script type="module">
  import { snapdom } from "https://unpkg.com/@zumer/snapdom@dev/dist/snapdom.mjs";
</script>

Build Outputs

| Variant | File | Use case | |---------|------|----------| | ESM (tree-shakeable) | dist/snapdom.mjs | Bundlers (Vite, webpack), import | | IIFE (global) | dist/snapdom.js | Script tag, legacy require |

Bundler (npm):

import { snapdom } from '@zumer/snapdom';  // → dist/snapdom.mjs

Script tag (CDN):

<script src="https://unpkg.com/@zumer/snapdom/dist/snapdom.js"></script>
<script> snapdom.toPng(document.body).then(img => document.body.appendChild(img)); </script>

Subpath imports (lighter bundle if you only need one):

import { preCache } from '@zumer/snapdom/preCache';
import { plugins } from '@zumer/snapdom/plugins';

Usage

| Pattern | When to use | |---------|-------------| | Reusable snapdom(el) | One clone → many exports (PNG + JPG + download). | | Shortcuts snapdom.toPng(el) | Single export, less code. |

Reusable capture

Capture once, export many times (no re-clone):

const el = document.querySelector('#target');
const result = await snapdom(el);

const img = await result.toPng();
document.body.appendChild(img);
await result.download({ format: 'jpg', filename: 'my-capture.jpg' });

One-step shortcuts

Direct export when you need a single format:

const png = await snapdom.toPng(el);
const blob = await snapdom.toBlob(el);
document.body.appendChild(png);

CORS & External Resources

When capturing elements that reference external stylesheets (e.g., Google Fonts, Font Awesome, or any CDN‑hosted CSS), you must ensure that the resources are served with proper CORS headers. Otherwise, the captured image may lack the expected fonts or icons, even though they render correctly in the browser.

Why is this needed?

  • Browsers block JavaScript (including SnapDOM) from reading the binary data of cross‑origin fonts or images unless the server explicitly allows it via Access-Control-Allow-Origin.
  • SnapDOM relies on Canvas, which enforces strict CORS policies — unlike the browser's rendering engine, which is more permissive for on‑screen display.

How to fix it

Add the crossorigin="anonymous" attribute to the <link> tag when loading external stylesheets:

<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
  crossorigin="anonymous"
/>

Note: If you are hosting the fonts or assets on the same origin as your page (e.g., using a local server like http://localhost), you do not need to add crossorigin – the browser treats them as same‑origin and allows full access.

Documentation

The full reference lives on snapdom.dev/docs — kept there so it stays in sync and searchable:

  • API reference — the snapdom() reusable object, shortcut methods, and exporter-specific options.
  • Options — every capture option (scale, dpr, embedFonts, useProxy, exclude/filter, compress, outerTransforms, outerShadows, cache…) explained with examples.
  • Plugins — build, register and ship custom plugins and export formats. Browse community plugins on the plugins page.
  • Cache & preCache — control caching between captures and preload resources.

API at a glance

snapdom(el, options?) returns a reusable object (toPng, toSvg, toCanvas, toBlob, toJpg, toWebp, download, url). For single exports, use the shortcuts:

| Method | Description | | ------------------------------ | --------------------------------- | | snapdom.toSvg(el, options?) | Returns an SVG HTMLImageElement | | snapdom.toCanvas(el, options?) | Returns a Canvas | | snapdom.toBlob(el, options?) | Returns an SVG or raster Blob | | snapdom.toPng(el, options?) | Returns a PNG image | | snapdom.toJpg(el, options?) | Returns a JPG image | | snapdom.toWebp(el, options?) | Returns a WebP image | | snapdom.download(el, options?) | Triggers a download |

Options at a glance

All options are optional and can be passed to snapdom(el, options) or any shortcut method.

| Option | Type | Default | Description | | ------ | ---- | ------- | ----------- | | scale | number | 1 | Output scale multiplier | | dpr | number | devicePixelRatio | Pixel density of the rasterized output | | width / height | number | null | Target output size (keeps aspect ratio if only one is set) | | backgroundColor | string | null (#ffffff for JPEG/WebP) | Background fill | | quality | number | 0.92 | JPEG/WebP quality (0–1) | | format | 'png' \| 'jpeg' \| 'webp' \| 'svg' | 'png' | Format for download() | | type | string | 'svg' | Blob type for toBlob() ('png', 'jpeg'…) | | filename | string | 'snapDOM' | Download filename | | embedFonts | boolean | false | Inline @font-face so text renders with your real fonts | | iconFonts | string \| RegExp \| array | [] | Icon font families (always embedded) | | localFonts | array | [] | Explicit fonts: { family, src, weight?, style? } | | excludeFonts | object | — | Skip fonts by family / domain / subset | | exclude | string[] | [] | CSS selectors to leave out of the capture | | filter | (el) => boolean | null | Keep-predicate (return false to drop a node) | | excludeMode / filterMode | 'hide' \| 'remove' | 'hide' | How excluded nodes are handled | | clip | 'viewport' \| {x, y, width, height} | null | Capture only a region; offscreen content is pruned | | compress | boolean | true | Downsample inlined images to their visible resolution | | useProxy | string | '' | CORS proxy prefix for cross-origin images | | fallbackURL | string \| fn | — | Fallback image for broken <img> | | cache | 'soft' \| 'auto' \| 'full' \| 'disabled' | 'soft' | Cache policy between captures | | outerTransforms | boolean | true | Keep root translate/rotate in the output | | outerShadows | boolean | false | Expand bounds to include root shadows/blur/outline | | fast | boolean | true | Skip idle delays for faster capture | | reconcile | boolean | false | Measure the clone against the live DOM and pin any diverging box to its real size. Fixes rare text re-wrap/layout drift at the cost of roughly doubling capture time — snapdom warns once (console.warn) if it detects a capture that could benefit from it | | burst | boolean | false | Memoizes repeated captures of this element via a scoped MutationObserver — an unchanged repeat skips the pipeline entirely. Without it, snapdom warns once if the same element is captured 3+ times within 2s | | invalidate | boolean | false | With burst: true, forces a fresh capture for changes automatic tracking can't see (canvas draws, programmatic CSSOM edits) | | plugins | array | — | Per-capture plugins (override globals by name) |

📖 Full API & every option, explained with examples → snapdom.dev/docs

Limitations

  • External images should be CORS-accessible (use useProxy option for handling CORS denied)
  • When WebP format is used on Safari, it will fallback to PNG rendering.
  • @font-face CSS rule is well supported, but if need to use JS FontFace(), see this workaround #43
  • Safari: captures with embedFonts or background/mask images run slower due to WebKit #219770 (font decode timing). SnapDOM does pre-captures + drawImage to prime the pipeline; configurable via safariWarmupAttempts (default 3).
  • Custom scrollbar styles (::-webkit-scrollbar): Applied only when the element has not been scrolled. When scrolled, the viewport content is captured without the scrollbar.

Performance Benchmarks

Setup. Vitest benchmarks on Chromium, repo tests. Hardware may affect results. Values are average capture time (ms) → lower is better.

Simple elements

| Scenario | SnapDOM current | SnapDOM v1.9.9 | html2canvas | html-to-image | | ------------------------ | --------------- | -------------- | ----------- | ------------- | | Small (200×100) | 0.5 ms | 0.8 ms | 67.7 ms | 3.1 ms | | Modal (400×300) | 0.5 ms | 0.8 ms | 75.5 ms | 3.6 ms | | Page View (1200×800) | 0.5 ms | 0.8 ms | 114.2 ms | 3.3 ms | | Large Scroll (2000×1500) | 0.5 ms | 0.8 ms | 186.3 ms | 3.2 ms | | Very Large (4000×2000) | 0.5 ms | 0.9 ms | 425.9 ms | 3.3 ms |

Complex elements

| Scenario | SnapDOM current | SnapDOM v1.9.9 | html2canvas | html-to-image | | ------------------------ | --------------- | -------------- | ----------- | ------------- | | Small (200×100) | 1.6 ms | 3.3 ms | 68.0 ms | 14.3 ms | | Modal (400×300) | 2.9 ms | 6.8 ms | 87.5 ms | 34.8 ms | | Page View (1200×800) | 17.5 ms | 50.2 ms | 178.0 ms | 429.0 ms | | Large Scroll (2000×1500) | 54.0 ms | 201.8 ms | 735.2 ms | 984.2 ms | | Very Large (4000×2000) | 171.4 ms | 453.7 ms | 1,800.4 ms | 2,611.9 ms |

Run the benchmarks

git clone https://github.com/zumerlab/snapdom.git
cd snapdom
npm install
npm run test:benchmark

Development

Source layout:

  • src/api/ – Public API (snapdom, preCache)
  • src/core/ – Capture pipeline, clone, prepare, plugins
  • src/modules/ – Images, fonts, pseudo-elements, backgrounds, SVG
  • src/exporters/ – toPng, toSvg, toBlob, etc.
  • dist/ – Build output (snapdom.js, snapdom.mjs, preCache.mjs, plugins.mjs)

Build:

git clone https://github.com/zumerlab/snapdom.git
cd snapdom
git checkout dev
npm install
npm run compile

Test:

npx playwright install   # Required for browser tests
npm test
npm run test:benchmark

For detailed guidelines, see CONTRIBUTING.

Contributors

Sponsors

Special thanks to @megaphonecolin, @sdraper69, @reynaldichernando, @gamma-app, @jrjohnson, and @ryanander for supporting this project!

If you'd like to support this project too, you can become a sponsor.

Show your support

If SnapDOM saved you time, a ⭐ on GitHub helps other developers find it — that's the whole ask.

Shipping something built with SnapDOM? Add the badge to your README:

Built with SnapDOM

[![Built with SnapDOM](https://img.shields.io/badge/built%20with-SnapDOM-blue)](https://snapdom.dev)

Projects using SnapDOM

SnapDOM runs in production across 290+ public repositories (GitHub dependents graph). A few notable ones, each verified from its own package.json:

See the full gallery at snapdom.dev/made-with. Shipping SnapDOM? Open a PR to add your project — real, verifiable projects only.

License

MIT © Zumerlab