logo
0
0
WeChat Login
github-actions[bot]<github-actions[bot]@users.noreply.github.com>
chore: update contributors list

snapDOM

NPM version GitHub contributors GitHub stars GitHub forks Sponsor tinchox5 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(options?): Promise<Blob>; toPng(options?): Promise<HTMLImageElement>; toJpg(options?): Promise<HTMLImageElement>; toWebp(options?): Promise<HTMLImageElement>; download(options?): Promise<void>; }

Shortcut methods

MethodDescription
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:

OptionTypeDefaultDescription
compressbooleantrueRemoves redundant styles
fastbooleantrueSkips idle delay for faster results
embedFontsbooleanfalseInlines fonts (icon fonts always embedded)
scalenumber1Output scale multiplier
widthnumber-Output specific width size
heightnumber-Output specific height size
backgroundColorstring"#fff"Fallback color for JPG/WebP
qualitynumber1Quality for JPG/WebP (0 to 1)
useProxystring''Specify a proxy for handling CORS images as fallback
typestringsvgSelect png, jpg, webp Blob type
excludestring[]-CSS selectors for elements to exclude
filterfunction-Custom filter function ie (el) => !el.classList.contains('hidden')

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 height) 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" or crossOrigin="use-credentials". In case fails to get the images, useProxy can be used to deal with CORS images:

const result = await snapdom(element, { useProxy: 'your/proxy/' //Example: 'https://corsproxy.io/?url=' or 'https://api.allorigins.win/raw?url=' });

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.
  • useProxy (string) — Proxy for handling CORS images as fallback.

Features

  • Captures shadow DOM and Web Components
  • Supports ::before, ::after and ::first-letter 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 shloud be CORS-accessible (use useProxy option for handling CORS denied)
  • 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

⚡ Performance Benchmarks

Snapdom has received significant performance improvements since version v1.8.0. The following benchmarks compare:

  • Snapdom (current)
  • Snapdom v1.8.0
  • html2canvas
  • html-to-image

Simple elements

ScenarioSnapdom (current)Snapdom v1.8.0html2canvashtml-to-image
Small (200×100)0.4 ms1.2 ms70.3 ms3.6 ms
Modal (400×300)0.4 ms1.1 ms68.8 ms3.6 ms
Page View (1200×800)0.4 ms1.0 ms100.5 ms3.4 ms
Large Scroll (2000×1500)0.4 ms1.0 ms153.1 ms3.4 ms
Very Large (4000×2000)0.4 ms1.0 ms278.9 ms4.3 ms

Complex elements

ScenarioSnapdom (current)Snapdom v1.8.0html2canvashtml-to-image
Small (200×100)1.1 ms3.2 ms76.0 ms15.3 ms
Modal (400×300)4.5 ms14.0 ms133.2 ms55.4 ms
Page View (1200×800)32.9 ms113.6 ms303.4 ms369.1 ms
Large Scroll (2000×1500)133.9 ms387.4 ms594.4 ms1,163.0 ms
Very Large (4000×2000)364.0 ms1,200.4 ms1,380.8 ms3,023.9 ms

Summary

  • Snapdom (current) is 2×–6× faster than v1.8.0
  • Up to 150× faster than html2canvas
  • Up to 8× faster than html-to-image in large scenarios

Benchmarks run in Chromium using Vitest.
Hardware: MacBook Air 2018.
⚠️ Performance may vary depending on device.

Run the benchmarks

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

Development

To contribute or build snapDOM locally:

# Clone the repository git clone https://github.com/zumerlab/snapdom.git cd snapdom # Switch to dev branch git checkout dev # Install dependencies npm install # Compile the library (ESM, CJS, and minified versions) npm run compile # Install playwright browsers (necessary for running tests) npx playwright install # 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 tarwin 17biubiu pedrocateexte domialex elliots jswhisperer simon1uo titoBouzout jhbae200 miusuncle rbbydotdev

💖 Sponsors

Special thanks to @megaphonecolin for supporting this project!

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

License

MIT © Zumerlab