SnapDOM Options
Set output size, load capture assets, exclude private content and control clipping. These options describe SnapDOM 3.
All options
All capture methods accept an options object.
| Option | Type | Default | Description |
|---|---|---|---|
| debug | boolean | false | Log fallbacks and errors to the console. Off, SnapDOM logs nothing. |
| scale | number | 1 | Output scale, used only when neither width nor height is set. |
| dpr | number | devicePixelRatio | Device pixel ratio for raster output. download() always uses 1; use scale for a sharper file. |
| 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 | true captures in one task; false pauses about every frame to keep the page responsive. '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 render engines. svg serializes the finished clone; html-in-canvas paints that same clone through the browser's native API (Chrome 157 and newer), with SVG fallback everywhere else. |
html-in-canvas runs where the browser exposes drawElementImage(): Chrome 157 and newer, without a flag. Firefox and Safari have no implementation. There, and on any capture the native engine cannot paint (body and html roots, transformed or zoomed ancestors, clip, width/height, outerShadows, plugins with render hooks), SnapDOM runs the SVG engine on the same clone, so the option is safe to set without a feature check. SVG stays the default because it is faster: the native paint waits for a rendering update. The guide says when to pick which; the lab compares both engines and lists which plugins run on each.
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 set. Requesting an SVG Blob from it rejects. Use engine: 'svg' when you need serialized SVG.
debug
SnapDOM logs nothing to the console by default. Set debug: true to log fallbacks and errors with the [SnapDOM] prefix: failed fetches, cross-origin iframes, downscaled exports, invalid exclude selectors. Without it, result.warnings records 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, which saves work when sources are much larger than 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, so later exports at a larger resolution still have it. Images that already fit are left alone. A native-engine capture is a bitmap, so enlarging it scales the captured pixels.
Fallback image on <img> load failure
Provide a default image for failed <img> loads: a fixed URL, or a callback that receives measured dimensions and returns a URL (handy for 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 also fails, the
<img>keeps its box and shows a grey placeholder, andresult.warningslists its URL. - The callback's width/height come from the original element (dataset, style/attrs, etc.) when available.
- The callback also gets
src, so it can send only the images that failed through your own proxy: see Capture images without CORS.
Dimensions (scale, width, height)
widthorheighttakes precedence overscale.dprstill multiplies raster pixels.- Only
width: height scales proportionally (and vice versa). - Both
widthandheight: exact size, may distort.
Cross-Origin Images & Fonts (useProxy)
Set useProxy to a proxy you trust for assets the browser cannot read directly:
await snapdom.toPng(el, {
useProxy: 'https://your-proxy.example/?url='
});
- When configured, the proxy handles cross-origin resource requests, including assets that already allow CORS. Same-origin and already-proxied URLs 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 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 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
Combine filter and exclude with independent layout modes. The CSS-effect filter plugin is a separate feature.
filter: a synchronous predicate. True keeps a node; 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.
// Hide private fields while preserving space; 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 match sets the mode and stops evaluation. The attribute uses excludeMode; a matching exclude wins even if filter would reject the same node with a different mode. A true filter result never overrides an exclusion.
Example: mixing selectors and a predicate:
await snapdom.toPng(el, {
exclude: ['.cookie-banner', (node) => node.dataset.secret === 'true'],
excludeMode: 'remove'
});
For selector-only exclusions, hide keeps the original space:
await snapdom.toPng(el, {
exclude: ['.cookie-banner', '.tooltip', '[data-test="debug"]']
});
Function-valued rules run on every new capture; an existing result keeps its content. See callbacks and memoization.
Other v2 option changes
When migrating from v2, remove these obsolete settings:
burst: remove this switch. Eligible repeat memoization is automatic. Useinvalidate: truefor one fresh capture after an otherwise unobservable change.compress: remove the switch. Bitmap optimization is automatic; there is no public opt-out to embed the original resources verbatim.resolvePicturePlaceholdersandpictureResolver: remove these settings. The clone resolves responsive images and common lazy-loading attributes. There is no replacement tuning object; handle custom loading, concurrency or timeouts in the application before capture.
fast: false remains supported. It now pauses about every frame instead of using idle callbacks; see fast. For removed APIs, TypeScript names and plugin-hook changes, see the migration guide.
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. 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 re-walk the tree and re-read every computed style on each call. SnapDOM memoizes eligible static elements from their first capture. Observable DOM, style, media and interaction changes invalidate the result; frame-driven content that cannot be observed reliably is captured fresh. A mutation triggers 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 bypass the memo. 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 skips the existing memo and clears style snapshots; a stable fresh result may become the new memo:
sheet.insertRule('.card { color: rebeccapurple }');
await snapdom(el, { invalidate: true });
Callbacks that read application state bypass this reuse; see the cache guide for the supported policies and an example.