MCPcopy Index your code
hub / github.com/zumerlab/snapdom

github.com/zumerlab/snapdom @v2.12.9 sqlite

repository ↗ · DeepWiki ↗ · release v2.12.9 ↗
467 symbols 1,204 edges 130 files 99 documented · 21% 6 cross-repo links
README

NPM version NPM weekly downloads GitHub contributors GitHub stars GitHub forks Sponsor tinchox5

License

English | 简体中文

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.

  • Full DOM capture
  • Embedded styles, pseudo-elements, and fonts
  • Export to SVG, PNG, JPG, WebP, canvas, or Blob
  • ⚡ Ultra fast, no dependencies
  • 100% based on standard Web APIs
  • Support same-origin ìframe
  • Support CSS counter() and CSS counters()
  • Support ... line-clamp

Demo

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' });

Capture Flow

SnapDOM transforms your DOM element through these stages:

DOM Element
    ↓
Clone
    ↓
Styles & Pseudo
    ↓
Images & Backgrounds
    ↓
Fonts
    ↓
SVG foreignObject
    ↓
data:image/svg+xml
    ↓
toPng / toSvg / toBlob / download
Stage What happens
Clone Deep clone with styles, Shadow DOM, iframes. Exclude/filter nodes.
Styles & Pseudo Inline ::before/::after as elements, resolve counter()/counters().
Images & Backgrounds Fetch and inline external images/backgrounds as data URLs.
Fonts Embed @font-face (optional) and icon fonts.
SVG Wrap clone in <foreignObject>, serialize to data:image/svg+xml.
Export Convert SVG to PNG/JPG/WebP/Blob or trigger download.

Plugin hooks: beforeSnapbeforeCloneafterClonebeforeRenderafterRenderbeforeExportafterExport.

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)


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


<script type="module">
  import { snapdom } from "https://unpkg.com/@zumer/snapdom/dist/snapdom.mjs";
</script>

CDN (dev builds)


<script src="https://unpkg.com/@zumer/snapdom@dev/dist/snapdom.js"></script>


<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);

API

snapdom(el, options?)

Returns an object with reusable export methods:

{
  url: string;
  toRaw(): string;
  toImg(): Promise<HTMLImageElement>; // deprecated 
  toSvg(): 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

Method Description
snapdom.toImg(el, options?) Returns an SVG HTMLImageElement (deprecated)
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

Exporter-specific options

Some exporters accept a small set of export-only options in addition to the global capture options.

download()

Option Type Default Description
filename string snapdom Download name.
format "png" \| "jpeg" \| "jpg" \| "webp" \| "svg" "png" Output format for the downloaded file.

Example:

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

toBlob()

Option Type Default Description
type "svg" \| "png" \| "jpeg" \| "jpg" \| "webp" "svg" Blob type to generate.

Example:

const blob = await result.toBlob({ type: 'jpeg', quality: 0.92 });

Options

All capture methods accept an options object:

Option Type Default Description
debug boolean false When true, logs suppressed errors to console.warn for troubleshooting
fast boolean true Skips small idle delays for faster results
embedFonts boolean false Inlines non-icon fonts (icon fonts always on)
localFonts array [] Local fonts { family, src, weight?, style? }
iconFonts string|RegExp|Array [] Extra icon font matchers
excludeFonts object {} Exclude families/domains/subsets during embedding
scale number 1 Output scale multiplier
dpr number devicePixelRatio Device pixel ratio
width number - Output width
height number - Output height
backgroundColor string "#fff" Fallback color for JPG/WebP
quality number 1 Quality for JPG/WebP (0 to 1)
useProxy string '' Proxy base for CORS fallbacks
exclude string[] - CSS selectors to exclude
excludeMode "hide"|"remove" "hide" How exclude is applied
filter function - Custom predicate (el) => boolean
filterMode "hide"|"remove" "hide" How filter is applied
cache string "soft" disabled | soft | auto | full
placeholders boolean true Show placeholders for images/CORS iframes
fallbackURL string | function - Fallback image for <img> load failure
outerTransforms boolean true When false removes translate/rotate but preserves scale/skew, producing a flat, reusable capture
outerShadows boolean false Do not expand the root’s bounding box for shadows/blur/outline, and strip those visual effects from the cloned root
safariWarmupAttempts number 3 Safari only: iterations to prime font/decode (WebKit #219770). Use 1 if 3 causes lag

debug

When debug: true, SnapDOM logs normally suppressed errors to console.warn (with the [snapdom] prefix). Useful for troubleshooting capture issues (canvas failures, blob resolution, style stripping, etc.) without noisy output in production.

await snapdom.toPng(el, { debug: true });

Fallback image on <img> load failure

Provide a default image for failed <img> loads. You can pass a fixed URL or a callback that receives measured dimensions and returns a URL (handy to generate dynamic placeholders).

// 1) Fixed URL fallback
await snapdom.toSvg(element, {
  fallbackURL: '/images/fallback.png'
});

// 2) Dynamic placeholder via callback
await snapdom.toSvg(element, {
  fallbackURL: ({ width: 300, height: 150 }) =>
    `https://placehold.co/${width}x${height}`
});

// 3) With proxy (if your fallback host has no CORS)
await snapdom.toSvg(element, {
  fallbackURL: ({ width = 300, height = 150 }) =>
    `https://dummyimage.com/${width}x${height}/cccccc/666.png&text=img`,
  useProxy: 'https://proxy.corsfix.com/?'
});

Notes: - If the fallback image also fails to load, snapDOM replaces the <img> with a placeholder block preserving width/height

Extension points exported contracts — how you extend this code

LocalFont (Interface)
(no doc)
types/snapdom.d.ts
ExcludeFonts (Interface)
(no doc)
types/snapdom.d.ts
SnapdomOptions (Interface)
(no doc)
types/snapdom.d.ts
CaptureContext (Interface)
(no doc)
types/snapdom.d.ts
SnapdomPlugin (Interface)
(no doc)
types/snapdom.d.ts

Core symbols most depended-on inside this repo

set
called by 86
src/core/cache.js
limitDecimals
called by 58
src/utils/capture.helpers.js
deepClone
called by 58
src/core/clone.js
captureDOM
called by 47
src/core/capture.js
snapFetch
called by 38
src/modules/snapFetch.js
embedCustomFonts
called by 32
src/modules/fonts.js
prepareClone
called by 30
src/core/prepare.js
createContext
called by 30
src/core/context.js

Shape

Function 418
Method 34
Interface 9
Class 6

Languages

TypeScript100%

Modules by API surface

src/modules/fonts.js37 symbols
types/snapdom.d.ts27 symbols
src/utils/clone.helpers.js27 symbols
src/utils/capture.helpers.js20 symbols
src/utils/transforms.helpers.js16 symbols
src/modules/pseudo.js15 symbols
src/modules/counter.js15 symbols
__tests__/utils.image.test.js15 symbols
src/utils/css.js14 symbols
src/modules/styles.js14 symbols
packages/plugins/gif-export.js13 symbols
__tests__/module.fonts.test.js13 symbols

Used by 2 indexed graphs manifest dependencies, hub-wide

Dependencies from manifests, versioned

@eslint/js9.36.0 · 1×
@vitest/browser3.1.2 · 1×
@vitest/coverage-v83.1.2 · 1×
@zumer/snapdiff0.1.1 · 1×
esbuild0.25.0 · 1×
eslint9.36.0 · 1×
globals16.4.0 · 1×
playwright1.52.0 · 1×

For agents

$ claude mcp add snapdom \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact