All options
All capture methods accept an options object.
| Option | Type | Default | Description |
|---|---|---|---|
| debug | boolean | false | Log suppressed errors to console.warn. |
| scale | number | 1 | Output scale, used only when neither width nor height is set. |
| dpr | number | devicePixelRatio | Device pixel ratio for raster output. |
| width / height | number | null | Absolute output size; one dimension preserves aspect ratio. |
| format | png | jpeg | jpg | webp | svg | png | Default for format-selecting exports; named helpers choose their codec. jpg resolves to jpeg. |
| type | png | jpeg | jpg | webp | svg | undefined | Deprecated alias kept synchronized with canonical format; prefer format, though either name is honored. |
| backgroundColor | string | null | null | Background fill; defaults to #ffffff only for JPEG/WebP. |
| quality | number | 0.92 | JPEG/WebP quality from 0 to 1. |
| filename | string | snapDOM | Download filename base. |
| embedFonts | boolean | "auto" | "auto" | Embed used webfonts; skip the font phase on system-font-only pages. true forces discovery; false skips text-font embedding. |
| localFonts | array | [] | Local fonts { family, src, weight?, style?, stretchPct? }. |
| iconFonts | string | RegExp | Array | [] | Extra icon-font matchers. |
| excludeFonts | object | undefined | Exclude font families, domains or subsets. |
| fontStylesheetDomains | string[] | [] | Extra domains allowed for cross-origin font CSS. |
| exclude | string | function | Array | [] | Selectors and/or predicates; returning true excludes the node. |
| excludeMode | "hide" | "remove" | "hide" | Keep an invisible spacer or remove the node. |
| filter | function | null | Inclusion predicate; true keeps a node, false filters it out. Can be used together with exclude. |
| filterMode | "hide" | "remove" | "hide" | Independent layout mode for nodes rejected by filter. |
| clip | "viewport" | rect | null | Capture only a viewport or page-coordinate rectangle. |
| useProxy | string | '' | CORS proxy template/base. |
| fallbackURL | string | function | undefined | Fallback for a broken <img>. A function reads current state when a new capture needs a fallback. |
| placeholders | boolean | true | Show placeholders for failed resources/CORS iframes. |
| outerTransforms | boolean | true | Keep the root's rotation and scale/skew. false removes rotation while preserving scale/skew; root translation is normalized. |
| outerShadows | boolean | "subtree" | false | Strip or bound root effects; "subtree" also bounds descendant shadow ink. Blur bleed is included unless clip fixes the edges. |
| reconcile | boolean | false | Measure and pin clone boxes that diverge from the live DOM. |
| fast | boolean | 'auto' | true | Whether a long capture may pause so the page keeps painting and taking input. 'auto' is experimental. |
| invalidate | boolean | false | Force one fresh capture that is not served from the existing memo and clear style snapshots after unobservable application changes. A stable fresh result may become the new memo. |
| captureSelection | boolean | false | Render the live text or field selection. |
| canvas | HTMLCanvasElement | null | Reuse an existing raster target. |
| excludeStyleProps | RegExp | function | null | Skip matching computed-style properties. A function is evaluated afresh for each new capture. |
| cache | "soft" | "disabled" | "auto" | "full" | false | "soft" | Controls persistent resource/style caches only. disabled/false clears and bypasses them; legacy values map to soft. Repeat memoization is separate. |
| plugins | array | undefined | Per-capture plugin definitions. |
| engine | "svg" | "html-in-canvas" | "svg" | Select one of SnapDOM's two integrated render engines. svg serializes the finished clone; experimental html-in-canvas paints that same clone through the browser's native API, with SVG fallback. |
html-in-canvas still requires the browser's experimental feature flag or applicable origin trial; supported Chrome builds expose it through chrome://flags/#canvas-draw-element. It also needs a build compiled with SNAPDOM_CANVAS_ENGINE=1 npm run compile; the default build currently omits it. Unsupported captures fall back to SVG, including geometry and render hooks the native engine cannot handle.
A successful native capture is a bitmap: url/toRaw() lazily encode PNG, toSvg() returns a PNG-backed image, and toBlob() defaults to PNG unless a format was explicitly selected. Requesting an SVG Blob from that bitmap rejects. Select engine: 'svg' when serialized SVG is required.
debug
Set debug: true to log suppressed errors with the [snapdom] prefix. The result also exposes warnings for recorded capture fallbacks and limits.
await snapdom.toPng(el, { debug: true });
Automatic image downsampling
SnapDOM reduces oversized raster assets to the resolution the requested output needs, accounting for output size, transforms and DPR. It preserves aspect ratio and never upscales the source. This covers <img>, captured canvas/video frames, non-repeating backgrounds and SVG <image>.
The benefit depends on the export:
- Raster exports decode smaller embedded images, reducing work when the source images greatly exceed their display size.
- SVG exports carry smaller image data.
urlandtoRaw()return that serialized capture without rasterizing it.
The SVG engine keeps the original asset data for later exports at a larger resolution. Images that already fit the requested resolution are left unchanged. A successful native-engine capture is already a bitmap, so enlarging it scales those captured pixels.
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. - Width/height used by the callback are gathered from the original element (dataset, style/attrs, etc.) when available.
Dimensions (scale, width, height)
- If
widthorheightis provided, it takes precedence overscale.dprstill multiplies raster pixels. - If only
widthis provided, height scales proportionally (and vice versa). - Providing both
widthandheightforces an exact size (may distort).
Cross-Origin Images & Fonts (useProxy)
SnapDOM fetches assets directly when their origin and CORS policy allow it. Set useProxy to a proxy you trust for assets the browser cannot read directly:
await snapdom.toPng(el, {
useProxy: 'https://your-proxy.example/?url='
});
- The proxy is only used as a fallback; same-origin and CORS-enabled assets skip it.
Fonts
embedFonts
The default, 'auto', embeds webfonts used by the capture and skips font discovery for system-font-only content. Use true to force discovery or false to skip text-font embedding. Icon-font glyphs use a separate rendering path.
localFonts
Declare font files or data URLs explicitly when stylesheet discovery cannot supply them:
await snapdom.toPng(el, {
embedFonts: true,
localFonts: [
{ family: 'Inter', src: '/fonts/Inter-Variable.woff2', weight: 400, style: 'normal' },
{ family: 'Inter', src: '/fonts/Inter-Italic.woff2', style: 'italic' }
]
});
iconFonts
Add custom icon families (names or regex matchers). Useful for private icon sets:
await snapdom.toPng(el, {
iconFonts: ['MyIcons', /^(Remix|Feather) Icons?$/i]
});
excludeFonts
Skip specific non-icon fonts to speed up capture or avoid unnecessary downloads.
await snapdom.toPng(el, {
embedFonts: true,
excludeFonts: {
families: ['Noto Serif', 'SomeHeavyFont'], // skip by family name
subsets: ['cyrillic-ext'] // skip by unicode-range subset tag
}
});
Notes
excludeFontsapplies to text-font embedding, not the icon-font rendering path.- Matching is case-insensitive for
families. Hosts are matched by substring against the resolved URL.
Selecting content: filter and exclude
filter and exclude remain independent, as in v2.x.x. Use them together in one capture, with separate layout modes. V3 adds optional predicates to exclude; this does not replace filter. The separate CSS-effect plugin named filter is also available.
filter: a synchronous boolean predicate. True keeps a node; false filters it out. As in v2, any falsy return filters it out.filterMode:hidekeeps an invisible spacer for filtered nodes;removedrops them and allows reflow. Defaults tohide.exclude: a selector, a synchronous boolean predicate, or any mix of both in an array. A node is excluded if any rule matches.excludeMode: the separate hide/remove mode for exclusions; defaults tohide. Both modes omit the affected node's content.
// Supported in v2 and v3: hide private fields, remove the toolbar.
await snapdom(el, {
filter: node => !node.matches('[data-private]'),
filterMode: 'hide',
exclude: ['.toolbar'],
excludeMode: 'remove'
});
Per node, data-capture="exclude" is checked first, then exclude, then filter. The first omission determines its mode and stops evaluation for that node. The attribute uses excludeMode; a matching exclude also wins if filter would reject the same node with a different mode. A true filter result does not override an exclusion.
Example: leave out elements with display:none:
/**
* @param {Element} el
* @returns {boolean} true = EXCLUDE this node (`filter` uses the opposite polarity)
*/
function isHidden(el) {
return window.getComputedStyle(el).display === 'none';
}
await snapdom.toPng(document.body, { exclude: isHidden });
Example: mixing selectors and a predicate:
await snapdom.toPng(el, {
exclude: ['.cookie-banner', (node) => node.dataset.secret === 'true'],
excludeMode: 'remove'
});
Example with exclude: remove banners or tooltips by selector
await snapdom.toPng(el, {
exclude: ['.cookie-banner', '.tooltip', '[data-test="debug"]']
});
Function-valued rules are evaluated when applicable on every new capture, including when the same function reads changing application state. They suspend unchanged-capture memoization; no invalidate is needed just because their closure changed. An already returned result keeps its original content until you capture again.
Other v2 option changes
Compared with v2.x.x, the following settings no longer belong to the public v3 options. The underlying capture capabilities remain:
burst: remove this switch. Eligible repeat memoization is automatic. Useinvalidate: truefor one fresh capture after an otherwise unobservable change.fast: falsestill keeps the page responsive, now by pausing about every frame instead of through idle callbacks. See fast.compress: remove the switch. Embedded bitmap optimization is automatic, with no public opt-out for embedding the original resources verbatim.resolvePicturePlaceholdersandpictureResolver: remove these settings. The clone resolves responsive images and common lazy-loading attributes. There is no replacement core tuning object; handle custom loading, concurrency or timeout requirements in the application before capture.
See the migration guide for removed APIs, TypeScript names and plugin-hook changes.
outerTransforms
outerTransforms: false removes the captured root's rotation while retaining its scale and skew. Root translation is normalized during capture. Descendant transforms keep their own layout.
outerTransforms: true (default)keeps the root's rotation and scale/skew, including individual CSS transforms.
outerShadows
outerShadows: false (default)— Strips rootbox-shadow,text-shadow,outline, anddrop-shadow(). It preservesfilter: blur(), whose bleed is included when the capture is not explicitly clipped.outerShadows: true— Keeps root shadow/outline/filter effects and expands the bounds for their ink.outerShadows: "subtree"— Also widens the capture for descendant shadow ink outside the root box, bounded by clipping ancestors.
Note: outerShadows: false does not promise a bleed-free box: authored blur() remains visible and its extent is still included. An explicit clip is the exception: its edges are exact and never expand for bleed.
Example
// Keep root transforms and include root shadow bleed
await snapdom.toSvg(el, { outerTransforms: true, outerShadows: true });
reconcile
With the SVG engine, captured HTML lays out inside <foreignObject>. Inline text and table cells can wrap differently when font metrics or layout constraints differ from the live page.
reconcile: true measures the styled clone offscreen against the live subtree and pins boxes that diverge. It adds a layout pass and can roughly double capture time. Use it when you see wrapping differences; SnapDOM also emits a one-time suggestion for captures that may benefit.
await snapdom.toPng(el, { reconcile: true });
fast
A capture reads the computed style of every node in one pass. On a large tree, such as a data table built from web components, that pass can hold the main thread for hundreds of milliseconds: the page stops painting and responding until it ends.
fast: false lets the capture give the page a turn about every frame, so scrolling, typing and animations continue while it runs. true, the default, captures in one task. The pauses add little to the total time.
fast: 'auto' is experimental and may change: it pauses only once a capture runs past 40 ms, so short captures never pause.
If the captured element changes while a capture is paused, SnapDOM clones it again in one task, so the image shows a state the page really had. That check uses the watchers behind repeat-capture memoization, so a capture that is never memoized (frame-driven trees, function-valued callbacks, captureSelection) keeps what it read.
await snapdom.toPng(table, { fast: false });
Automatic repeat-capture memoization
Capturing the same unchanged element repeatedly would otherwise re-walk the tree and re-read every node's computed style on every call. SnapDOM memoizes eligible static elements from their first capture. Observable DOM, style, media and interaction changes invalidate that result; frame-driven content that cannot be observed reliably is captured fresh. A mutation may trigger a conservative full capture or a differential rebuild when that rebuild is provably equivalent. At most 64 elements stay memoized; the least recently captured one is released.
// Stable captures can reuse the result; the text mutation invalidates it.
for (let i = 0; i < 10; i++) {
if (i === 3) counter.textContent = 'new value';
const result = await snapdom(el);
img.src = (await result.toPng()).src;
}
Frame-driven canvas/video/iframe trees already bypass the memo and capture fresh. Programmatic CSSOM edits (stylesheet.insertRule/deleteRule, cssRule.style.* on a rule rather than an element) expose no browser signal, so pass invalidate: true on the next call. That call is not served from the existing memo and clears style snapshots; if its fresh result is stable, it may become the new memo:
sheet.insertRule('.card { color: rebeccapurple }');
await snapdom(el, { invalidate: true });
Function-valued filter, exclude, excludeStyleProps or fallbackURL already force a new capture and reevaluate applicable callback decisions. A changed closure needs no invalidate. Previous style/fallback decisions are not reused for those callbacks. Exporting an existing result still uses its original captured state.
Ready to capture?
Try the browser demo, then use the API reference to choose your output.
Open the demo Install from npm