Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@zumer/snapdom

zumerlab1.9kMIT1.8.0TypeScript support: included

snapDOM captures HTML elements to images with exceptional speed and accuracy.

zumerlab, snapDOM, screenshot, engine, html capture, dom capture, html to image, dom to image, html screenshot, capture element, html snapshot, element screenshot, web capture, snapshot tool, render html, capture dom, web snapshot, html export, dom snapshot, html to png, html to svg

readme

snapDOM

NPM version GitHub contributors GitHub stars GitHub forks License

snapDOM is a fast and accurate DOM-to-image capture tool built for Zumly, a zoom-based view transition framework.

It captures any HTML element as a scalable SVG image, preserving styles, fonts, background images, pseudo-elements, and even shadow DOM. It also supports export to raster image formats and canvas.

  • 📸 Full DOM capture
  • 🎨 Embedded styles, pseudo-elements, and fonts
  • 🖼️ Export to SVG, PNG, JPG, WebP, or canvas
  • ⚡ Ultra fast, no dependencies
  • 📦 100% based on standard Web APIs

Demo

https://zumerlab.github.io/snapdom/

Installation

NPM / Yarn

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

CDN

<script src="https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.min.js"></script>

Script tag (local)

<script src="snapdom.js"></script>

ES Module

import { snapdom } from './snapdom.mjs';

Module via CDN

<script type="module">
  import { snapdom } from 'https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.mjs';
</script>

Basic usage

Reusable capture

const el = document.querySelector('#target');
const result = await snapdom(el, { scale: 2 });

const img = await result.toPng();
document.body.appendChild(img);

await result.download({ format: 'jpg', filename: 'my-capture' });

One-step shortcuts

const el = document.querySelector('#target');
const png = await snapdom.toPng(el);
document.body.appendChild(png);

const blob = await snapdom.toBlob(el);

API

snapdom(el, options?)

Returns an object with reusable export methods:

{
  url: string;
  toRaw(): string;
  toImg(): Promise<HTMLImageElement>;
  toCanvas(): Promise<HTMLCanvasElement>;
  toBlob(): Promise<Blob>;
  toPng(): Promise<HTMLImageElement>;
  toJpg(options?): Promise<HTMLImageElement>;
  toWebp(options?): Promise<HTMLImageElement>;
  download(options?): Promise<void>;
}

Shortcut methods

Method Description
snapdom.toImg(el, options?) Returns an HTMLImageElement
snapdom.toCanvas(el, options?) Returns a Canvas
snapdom.toBlob(el, options?) Returns an SVG 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 download in specified format

Options

All capture methods accept an options object:

Option Type Default Description
compress boolean true Removes redundant styles
fast boolean true Skips idle delay for faster results
embedFonts boolean false Inlines fonts (icon fonts always embedded)
scale number 1 Output scale multiplier
width number - Output specific width size
height number - Output specific height size
backgroundColor string "#fff" Fallback color for JPG/WebP
quality number 1 Quality for JPG/WebP (0 to 1)
crossOrigin function - Function to determine CORS mode per image URL

Setting custom dimensions with width and height options

Use the width and height options to generate an image with specific dimensions.

Examples:

1. Fixed width (proportional Hhight) Sets a specific width while maintaining the aspect ratio. Height adjusts proportionally.

const result = await snapdom(element, {
  width: 400 // Outputs a 400px-wide image with auto-scaled height
});

2. Fixed height (proportional width) Sets a specific height while maintaining the aspect ratio. Width adjusts proportionally.

const result = await snapdom(element, {
  height: 200 // Outputs a 200px-tall image with auto-scaled width
});

3. Fixed width and height (may distort image) Forces exact dimensions, potentially distorting the image if the aspect ratio differs.

const result = await snapdom(element, {
  width: 800,  // Outputs an 800px × 200px image (may stretch/squish content)
  height: 200  
});

Note: If scale is different from 1, it takes priority over width and height. Example: { scale: 3, width: 500 } ignores width and scales the image 3x instead.

Cross-Origin Images

By default, snapDOM loads images with crossOrigin="anonymous". You can customize this behavior using the crossOrigin option:

const result = await snapdom(element, {
  crossOrigin: (url) => {
    // Use credentials for same-origin images
    if (url.startsWith(window.location.origin)) {
      return "use-credentials";
    }
    // Use anonymous for cross-origin images
    return "anonymous";
  }
});

This is useful when your images require authentication or when dealing with credentialed requests.

Download options

{
  format?: "svg" | "png" | "jpg" | "jpeg" | "webp"; // default: "png"
  filename?: string;         // default: "capture"
  backgroundColor?: string;  // optional override
}

preCache() – Optional helper

The preCache() function can be used to load external resources (like images and fonts) in advance. It is specially useful when the element to capure is big and complex.

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

await preCache(document.body);
import { snapdom, preCache } from './snapdom.mjs';
    window.addEventListener('load', async () => {
    await preCache();
    console.log('📦 Resources preloaded');
    });

Options for preCache():

  • embedFonts (boolean, default: true) — Inlines non-icon fonts during preload.
  • reset (boolean, default: false) — Clears all existing internal caches.
  • crossOrigin (function) — Function to determine CORS mode per image URL during preload.

Features

  • Captures shadow DOM and Web Components
  • Supports ::before and ::after pseudo-elements
  • Inlines background images and fonts
  • Handles Font Awesome, Material Icons, and more
  • data-capture="exclude" to ignore an element
  • data-capture="placeholder" with data-placeholder-text for masked replacements

Limitations

  • External images must be CORS-accessible (use crossOrigin option for credentialed requests)
  • Iframes are not supported
  • 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

Benchmarks

snapDOM is not only highly accurate — it’s extremely fast.

Latest benchmarks show significant performance improvements against other libraries:

Scenario vs. modern-screenshot vs. html2canvas
Small element (200×100) 6.46× faster 32.27× faster
Modal size (400×300) 7.28× faster 32.66× faster
Page view (1200×800) 13.17× faster 35.29× faster
Large scroll area (2000×1500) 38.23× faster 68.85× faster
Very large element (4000×2000) 93.31× faster 133.12× faster
Complex small element (200×100) 3.97× faster 15.23× faster
Complex modal (400×300) 2.32× faster 5.33× faster
Complex page (1200×800) 1.62× faster 1.65× faster
Complex large scroll (2000×1500) 1.66× faster 1.24× faster
Complex very large (4000×2000) 1.52× faster 1.28× faster

Run the benchmarks

To run these benchmarks yourself:

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

They execute in headless Chromium using real DOM nodes.

Development

To contribute or build snapDOM locally:

# Clone the repository
git clone https://github.com/zumerlab/snapdom.git
cd snapdom

# Install dependencies
npm install

# Compile the library (ESM, CJS, and minified versions)
npm run compile

# Run tests
npm test

# Run Benchmarks
npm run test:benchmark

The main entry point is in src/, and output bundles are generated in the dist/ folder.

For detailed contribution guidelines, please see CONTRIBUTING.

Contributors 🙌

tinchox5 pedrocateexte domialex elliots jswhisperer jhbae200

License

MIT © Zumerlab

changelog

Changelog

All notable changes to this project will be documented in this file.

v1.8.0

30 June 2025

  • Fix: encode same uri multiple times. Thanks @pedrocate! #65
  • Avoid background-image logic duplication, closes #66 #66
  • Improve split multiple backgrounds 0e67a9b
  • Fix background image handling, closes #57 #57
  • Feat: sanitize rootElement to avoid CSS layout conflicts. Fixes #56, fixes #24 #56 #24
  • Clean transform RootElement prop f293e5b
  • Fix: canvas style props, closes #63 #63
  • Feat: handling @import and optimice cache, closes #61 #61
  • Compile .js to es2015, closes #58 #58
  • Improve inlinePseudoElements() to handle decorative properties, closes #55 #55
  • Add ::first-letter detection, closes #52 #52
  • Add Lucide to icon font detection. Thanks @domialex! #50

v1.7.1

19 June 2025

  • Improve inlineBackgroundImages() to support multiple background-image values. Thanks @jhbae200! 95a5490
  • Add @font-face / FontFace() deteccion, closes #43 #43
  • Add options.crossOrigin. Thanks @elliots! 49f8ac6
  • Fix prevent erasing non url background 0d626cb

v1.3.0

14 June 2025

  • fix: double scaled images #38
  • Fix: background img & img base64 in pseudo elements, closes #36 #36
  • Feat: captures input values, closes #35 #35
  • Improve: Device Pixel Ratio handling, thanks @jswhisperer 1a14f69
  • Bumped version 489be08
  • Update description 4db784b

v1.2.5

9 June 2025

  • Fix duplicated font-icon when embedFonts is true, closes #30 #30
  • Fix url with encode url, closes #29 #29
  • Fix .toCanvas scale fb47284
  • Bumped version 75b917a
  • Update cdn 37533a2
  • add homepage aa85c5d

v1.2.2

4 June 2025

  • Patch: type script definitions, closes #23 #23
  • Bumped version 548d7f3

v1.2.1

31 May 2025

  • feat(embedFonts): also embed icon fonts when embedFonts is true #18
  • Fix expose snapdom and preCache on browser compilation, closes #26 #26
  • Improve icon-font conversion 7bac4ee
  • Fix compress mode 652cfe9
  • Bumped version 0cd7973
  • Remove some logs 4348b39
  • Chore: delete old comments ff81a40
  • Chore: add dry bump script 5c421c7

v1.1.0

28 May 2025

v1.0.0

19 May 2025

v1.0.0-pre.1747581859131

18 May 2025

  • Fix retina and scale bug, closes #15 #15
  • Improve public API, closes #16 #16
  • Fix bug to render canvas with precache compress mode, closes #13 #13
  • Update to reflect new public API b6024cb
  • Update tests and benckmarks f06a0f8
  • Add helper to check Safari 6c9ee04
  • Remove preWarm d3bd582
  • Bumped version fb0855d

v0.9.9

14 May 2025

v0.9.8

14 May 2025

v0.9.7

14 May 2025

v0.9.2

1 May 2025

v0.9.2-pre.1746130901718

1 May 2025

  • This PR dramatically improves the speed and accuracy of snapDOM. It increases the result size and may produce some long tasks, but it provides a solid foundation to address these side effects in the future. #6
  • Add as draft new default approach - not implemented 6f4ec41
  • Add tests bdd5a7f
  • Feat: captures icon fonts 7b39e5f
  • Omit process default styles - temporary 2953196
  • update to v.0.9.2 e0179a1
  • Add options as Object and allow bgColor on jpg and webp e5abaa7
  • Update commented docs cfd2272
  • Update README.md fef6751
  • update 26ff7ea
  • Update README.md 3fda999
  • Update README.md 8ee616b
  • Omit delay function - temporary 0f04721
  • update unpkg url 13ce66b
  • Bumped version 9e4f518
  • Update README.md 3733476
  • Update README.md 00c74b0
  • Update README.md dd2c9c5
  • Update README.md d271cf7
  • Update README.md 02bf650

v0.9.1

27 April 2025

v0.9.0

27 April 2025