dom-to-image-more
Turn any DOM node into a PNG, JPEG, SVG, or canvas image entirely in the browser, no server or screenshot service required.
Repository Health
Technical Analysis
dom-to-image-more is a browser library that renders an arbitrary DOM node (including same-origin and blob iframes) into a raster (PNG/JPEG) or vector (SVG) image using an SVG foreignObject and HTML5 canvas, all client-side with no server round trip. It is a long-running, actively maintained fork of Anatolii Saienko’s original dom-to-image (itself built on Paul Bakaus’s domvas), carrying forward the original approach while fixing a long list of fidelity bugs and adding features the original never gained.
The library walks and deep-clones the target node, inlines every external dependency the clone needs to render standalone — images, @font-face web fonts, CSS background/mask URLs, and nested SVG <use>/<image> references — then serializes the clone into an SVG data URI and draws it to a canvas. A requestInterceptor hook unifies that whole resource-fetching path, letting callers supply or recover any external resource before or after a fetch attempt, with per-resource-type fallbacks instead of one global placeholder.
Beyond the resource pipeline, it exposes fine-grained rendering controls: adjustClonedNode and onclone hooks for mutating the clone before capture, adjustPseudoElement for editing or dropping ::before/::after content, pixelRatio for high-DPI output, preserveScroll to keep nested scroll positions instead of resetting them, and filterUrls/filterStyles for selectively excluding resources or CSS properties from the output. It ships a bundled TypeScript definition file and is validated against real Chrome and Firefox via Karma, plus a dedicated SSR smoke test and tsc-based type tests.
What You Get
toPng/toJpeg/toSvg/toCanvas/toBlob/toPixelData— six output functions covering raster, vector, canvas, blob, and raw-pixel capture, all returning promisesrequestInterceptor— a single hook to supply or recover any external image/font/stylesheet resource, consulted both before the fetch and on failure- Web font and image inlining — discovers
@font-facerules (including in cross-origin stylesheets via opt-inloadExternalStyleSheet) and inlines fonts and images as data URIs so the output is self-contained - Clone-time hooks —
adjustClonedNode,onclone, andadjustPseudoElementfor mutating or filtering the cloned tree, including dropping or rewriting::before/::aftercontent - High-DPI and scroll fidelity —
pixelRatiofor crisp Retina output andpreserveScrollto reflect each element’s actual scroll position instead of resetting it - Bundled TypeScript definitions — a hand-maintained
.d.tsvalidated by its owntsc --stricttype-test suite
Common Use Cases
- Exporting a chart, dashboard widget, or report section as a downloadable PNG/JPEG image
- Generating a shareable image snapshot of user-generated content (e.g. a card, invoice, or certificate) without a server-side screenshot pipeline
- Capturing SVG output of arbitrary HTML for further vector editing or embedding
- Building client-side ‘export as image’ or ‘copy as image’ features in web apps that must work entirely offline/client-side
Under The Hood
Architecture
The entire library lives in a single file (src/dom-to-image-more.js) organized as a set of factory functions — newUtil, newInliner, newFontFaces, newImages — each returning a closures-based object exposing a narrow set of methods, invoked once at module load to build shared singleton helpers (util, inliner, fontFaces, images) used throughout the six public entry points (toSvg, toPng, toJpeg, toBlob, toCanvas, toPixelData). The render pipeline is linear and easy to trace: draw() clones the target node via cloneNode() (which recursively walks children, computed styles, and pseudo-elements), embedFonts()/inlineImages() replace every external reference with inlined data URIs through the requestInterceptor hook, the result is serialized to an SVG data URI, and that SVG is drawn to a canvas sized and scaled per pixelRatio. Nothing in the core pipeline can be swapped without editing the file directly — there’s no plugin registry — but the hook surface (adjustClonedNode, onclone, adjustPseudoElement, filterUrls, filterStyles, requestInterceptor) gives callers well-defined seams into each stage without needing to fork the core traversal.
Tech Stack
The library has zero runtime dependencies and targets the browser DOM/Canvas/SVG APIs directly with no framework. It ships as a UMD bundle (dist/dom-to-image-more.min.js, built via Grunt/grunt-contrib-uglify) alongside a hand-maintained dom-to-image-more.d.ts for TypeScript consumers. Development tooling is ESLint (flat config, eslint:recommended plus a no-unused-vars rule set) and Prettier for formatting, Karma with the Mocha/Chai/karma-chrome-launcher/karma-firefox-launcher stack for real-browser test execution, and a small Node-based SSR smoke test to catch server-side-rendering breakage. TypeScript itself is used only as a devDependency to type-check the bundled .d.ts against a dedicated test-definitions file.
Code Quality
Testing is extensive relative to the library’s size: a single ~4,000-line Karma/Mocha/Chai spec (spec/dom-to-image-more.spec.js) exercises the rendering pipeline against dozens of fixture pages under spec/resources/ (fonts, iframes, shadow DOM, SVG namespaces, scrolling, pseudo-elements, and more), run against actual Chrome and Firefox rather than a DOM-emulation layer. A separate test-node/ssr-smoke.js verifies the library fails cleanly under Node/SSR instead of throwing a raw ReferenceError, and test-d/dom-to-image-more.test-d.ts type-checks the public API surface via tsc --strict. Error handling favors graceful degradation over throwing — failed resource fetches resolve to an empty string or a placeholder rather than rejecting the whole render — and diagnostics are routed through a replaceable logger option instead of hardcoded console calls. The single large source file and use of var/closures rather than ES classes or modules is dated by modern standards, but naming is consistent and the flat-config ESLint plus Prettier setup is actively enforced in CI (lint script, --max-warnings=0).
What Makes It Unique
Unlike libraries that rasterize by walking layout boxes or driving a headless browser, dom-to-image-more’s SVG-foreignObject approach lets the browser’s own layout and paint engine render the cloned DOM tree, which is what gives it fidelity for CSS features (masks, pseudo-elements, web fonts, nested SVG) that box-model reimplementations tend to miss. Its most distinctive addition versus the upstream project it forked from is the unified requestInterceptor hook, which collapses what used to be separate ad hoc image/font-fetching code paths into one consistently-applied interception point with per-resource-type fallback behavior — giving callers a single place to add proxying, caching, or CORS workarounds instead of patching multiple internal fetch call sites.