dom-to-image, html-to-image, and html2canvas are browser-based libraries that convert DOM elements into image formats like PNG or JPEG by rendering HTML and CSS onto an HTML canvas. They enable client-side generation of visual snapshots of web content without requiring a server or headless browser. screenshot-desktop, in contrast, is a Node.js package that captures screenshots of the actual desktop or application windows using native operating system APIs—it does not render HTML and is unrelated to in-browser DOM manipulation. These tools serve fundamentally different purposes: the first three are for programmatic rendering of web content, while the last is for desktop automation and system-level screen capture.
Capturing visual representations of web content is a common need — from generating social media previews to creating PDF reports or saving user-designed layouts. The four packages under review solve overlapping but distinct problems in this space. Let’s break down their technical approaches, capabilities, and trade-offs.
dom-to-image, html-to-image, and html2canvas all convert DOM elements into raster images (PNG, JPEG, etc.) directly in the browser using JavaScript. They do not require a headless browser or external process.
screenshot-desktop, by contrast, captures screenshots of the entire desktop or specific windows — it runs only in Node.js and requires system-level access. It cannot render arbitrary HTML; it only snapshots what’s already visible on screen.
⚠️ Important:
screenshot-desktopis not a web content renderer. If your goal is to turn HTML/CSS into an image programmatically (e.g., for a chart, dashboard widget, or user-generated design),screenshot-desktopis not applicable.
All three browser-based libraries parse the DOM, apply computed styles, and draw the result onto an HTML <canvas>. However, their implementation strategies differ significantly.
html2canvas: Pixel-Perfect Simulation via Canvas Replayhtml2canvas attempts to replay the rendering process by walking the DOM tree, reading computed styles, and manually redrawing everything onto a canvas using the Canvas 2D API. It supports many CSS features (like border-radius, box-shadow, transforms) but has known limitations with complex layouts (e.g., flex, grid, clip-path).
// html2canvas: Basic usage
import html2canvas from 'html2canvas';
const element = document.getElementById('capture');
html2canvas(element).then(canvas => {
const imgData = canvas.toDataURL('image/png');
// Use imgData as needed
});
It offers fine-grained control via options like scale, useCORS, and logging:
html2canvas(element, {
scale: 2,
useCORS: true,
logging: false
}).then(canvas => { /* ... */ });
dom-to-image: SVG Serialization + Rasterizationdom-to-image takes a different approach: it serializes the target node to an inline SVG, embedding fonts and external resources as data URLs, then draws that SVG onto a canvas. This often yields better fidelity for vector-like content (text, icons, simple shapes) but struggles with advanced CSS effects that don’t translate cleanly to SVG.
// dom-to-image: Basic usage
import domtoimage from 'dom-to-image';
const node = document.getElementById('capture');
domtoimage.toPng(node)
.then(dataUrl => {
// Use dataUrl
})
.catch(error => console.error('Error:', error));
It provides multiple output formats (toPng, toJpeg, toSvg, toCanvas) and supports filtering nodes:
const filter = (node) => !(node instanceof HTMLIFrameElement);
domtoimage.toPng(node, { filter });
🔍 Note:
dom-to-imageappears to be unmaintained. Its npm page shows no recent updates, and the GitHub repo (tsayen/dom-to-image) has unresolved issues and open pull requests dating back years. Use with caution in new projects.
html-to-image: A Modern Fork of dom-to-imagehtml-to-image is a community-maintained fork of dom-to-image that addresses many bugs and adds new features (like better font handling, improved CORS support, and TypeScript definitions). It uses the same SVG serialization strategy but with active development and better reliability.
// html-to-image: Basic usage
import { toPng } from 'html-to-image';
const node = document.getElementById('capture');
toPng(node)
.then(dataUrl => {
// Use dataUrl
})
.catch(error => console.error('Error:', error));
It also supports custom styling overrides and pixel ratio control:
const options = {
pixelRatio: 2,
style: { margin: '0' }
};
toPng(node, options);
screenshot-desktop operates entirely outside the browser context. It leverages native OS APIs (via Node.js bindings) to capture screenshots. It’s useful for automated UI testing, desktop app monitoring, or capturing full-screen content — but not for rendering dynamic HTML.
// screenshot-desktop: Capture entire screen
import screenshot from 'screenshot-desktop';
screenshot().then(imgBuffer => {
// imgBuffer is a Buffer containing PNG data
});
// Or capture a specific window by title
screenshot({
screen: 0,
format: 'png',
filename: 'output.png'
});
Key limitations:
html2canvas: Supports images via useCORS: true but may fail with cross-origin fonts unless properly configured.dom-to-image: Embeds images/fonts as data URLs but often fails silently with CORS-protected assets.html-to-image: Improved CORS handling; can proxy external resources through a server if needed.screenshot-desktop: N/A — it captures pixels as displayed by the OS.html2canvas: Best for basic layout and styling. Struggles with modern CSS (e.g., backdrop-filter, complex gradients).dom-to-image / html-to-image: Better with text and vector graphics, but limited with CSS-only effects (shadows, blends).html2canvas: Can be slow on large/complex DOM trees due to deep traversal and canvas drawing.html-to-image: Generally faster than dom-to-image due to optimizations, but still blocks the main thread.screenshot-desktop: Very fast (native OS call), but irrelevant for in-browser rendering.html2canvashtml-to-imagedom-to-imagehtml-to-image instead.z-index handling).screenshot-desktopIf you’re currently using dom-to-image, migrating to html-to-image is straightforward:
// Before (dom-to-image)
import domtoimage from 'dom-to-image';
domtoimage.toPng(node);
// After (html-to-image)
import { toPng } from 'html-to-image';
toPng(node);
The API is nearly identical, but html-to-image exports individual functions instead of a single object.
| Package | Environment | Rendering Method | Actively Maintained? | Best For |
|---|---|---|---|---|
html2canvas | Browser | Canvas replay | ✅ Yes | General-purpose DOM capture with CSS support |
html-to-image | Browser | SVG serialization | ✅ Yes | Text/vector content; modern alternative |
dom-to-image | Browser | SVG serialization | ❌ No | Legacy projects only |
screenshot-desktop | Node.js | OS-level screenshot | ✅ Yes | Desktop automation, not web content rendering |
html-to-image for most new projects — it’s reliable, maintained, and handles common cases well.html2canvas if you’re dealing with complex CSS layouts that don’t translate well to SVG.dom-to-image in new codebases.screenshot-desktop only when you genuinely need to capture desktop pixels — never for rendering HTML programmatically.Avoid dom-to-image in new projects — it appears unmaintained, with unresolved bugs and no recent updates. If you're already using it and it works for your use case, consider migrating to html-to-image, which is a well-maintained fork with the same API but better reliability and active development.
Choose html-to-image when you need a modern, actively maintained library for converting DOM elements to images in the browser, especially for content heavy in text, icons, or vector graphics. It handles fonts and external resources more reliably than older alternatives and offers good TypeScript support.
Choose html2canvas when you're capturing moderately complex UIs that rely on standard CSS layout and styling, and you need fine-grained control over the rendering process. It’s a solid choice if your content includes effects like shadows or borders that may not serialize well to SVG.
Choose screenshot-desktop only when you need to capture actual desktop or application window screenshots from a Node.js environment—such as in desktop automation, UI testing of native apps, or system monitoring. It cannot render arbitrary HTML and should never be used for in-browser DOM-to-image conversion.
dom-to-image is a library which can turn arbitrary DOM node into a vector (SVG) or raster (PNG or JPEG) image, written in JavaScript. It's based on domvas by Paul Bakaus and has been completely rewritten, with some bugs fixed and some new features (like web font and image support) added.
npm install dom-to-image
Then load
/* in ES 6 */
import domtoimage from 'dom-to-image';
/* in ES 5 */
var domtoimage = require('dom-to-image');
bower install dom-to-image
Include either src/dom-to-image.js or dist/dom-to-image.min.js in your page
and it will make the domtoimage variable available in the global scope.
<script src="path/to/dom-to-image.min.js" />
<script>
domtoimage.toPng(node)
//...
</script>
All the top level functions accept DOM node and rendering options,
and return promises, which are fulfilled with corresponding data URLs.
Get a PNG image base64-encoded data URL and display right away:
var node = document.getElementById('my-node');
domtoimage.toPng(node)
.then(function (dataUrl) {
var img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
.catch(function (error) {
console.error('oops, something went wrong!', error);
});
Get a PNG image blob and download it (using FileSaver, for example):
domtoimage.toBlob(document.getElementById('my-node'))
.then(function (blob) {
window.saveAs(blob, 'my-node.png');
});
Save and download a compressed JPEG image:
domtoimage.toJpeg(document.getElementById('my-node'), { quality: 0.95 })
.then(function (dataUrl) {
var link = document.createElement('a');
link.download = 'my-image-name.jpeg';
link.href = dataUrl;
link.click();
});
Get an SVG data URL, but filter out all the <i> elements:
function filter (node) {
return (node.tagName !== 'i');
}
domtoimage.toSvg(document.getElementById('my-node'), {filter: filter})
.then(function (dataUrl) {
/* do something */
});
Get the raw pixel data as a Uint8Array with every 4 array elements representing the RGBA data of a pixel:
var node = document.getElementById('my-node');
domtoimage.toPixelData(node)
.then(function (pixels) {
for (var y = 0; y < node.scrollHeight; ++y) {
for (var x = 0; x < node.scrollWidth; ++x) {
pixelAtXYOffset = (4 * y * node.scrollHeight) + (4 * x);
/* pixelAtXY is a Uint8Array[4] containing RGBA values of the pixel at (x, y) in the range 0..255 */
pixelAtXY = pixels.slice(pixelAtXYOffset, pixelAtXYOffset + 4);
}
}
});
All the functions under impl are not public API and are exposed only
for unit testing.
A function taking DOM node as argument. Should return true if passed node should be included in the output (excluding node means excluding it's children as well). Not called on the root node.
A string value for the background color, any valid CSS color value.
Height and width in pixels to be applied to node before rendering.
An object whose properties to be copied to node's style before rendering. You might want to check this reference for JavaScript names of CSS properties.
A number between 0 and 1 indicating image quality (e.g. 0.92 => 92%) of the JPEG image. Defaults to 1.0 (100%)
Set to true to append the current time as a query string to URL requests to enable cache busting. Defaults to false
A data URL for a placeholder image that will be used when fetching an image fails. Defaults to undefined and will throw an error on failed images
It's tested on latest Chrome and Firefox (49 and 45 respectively at the time
of writing), with Chrome performing significantly better on big DOM trees,
possibly due to it's more performant SVG support, and the fact that it supports
CSSStyleDeclaration.cssText property.
Internet Explorer is not (and will not be) supported, as it does not support
SVG <foreignObject> tag
Safari is not supported, as it uses a stricter security model on <foreignObject> tag. Suggested workaround is to use toSvg and render on the server.`
Only standard lib is currently used, but make sure your browser supports:
<foreignObject> tagMost importantly, tests depend on:
js-imagediff, to compare rendered and control images
ocrad.js, for the parts when you can't compare images (due to the browser rendering differences) and just have to test whether the text is rendered
There might some day exist (or maybe already exists?) a simple and standard way of exporting parts of the HTML to image (and then this script can only serve as an evidence of all the hoops I had to jump through in order to get such obvious thing done) but I haven't found one so far.
This library uses a feature of SVG that allows having arbitrary HTML content
inside of the <foreignObject> tag. So, in order to render that DOM node
for you, following steps are taken:
Clone the original DOM node recursively
Compute the style for the node and each sub-node and copy it to corresponding clone
Embed web fonts
find all the @font-face declarations that might represent web fonts
parse file URLs, download corresponding files
base64-encode and inline content as data: URLs
concatenate all the processed CSS rules and put them into one <style>
element, then attach it to the clone
Embed images
embed image URLs in <img> elements
inline images used in background CSS property, in a fashion similar to
fonts
Serialize the cloned node to XML
Wrap XML into the <foreignObject> tag, then into the SVG, then make it a
data URL
Optionally, to get PNG content or raw pixel data as a Uint8Array, create an Image element with the SVG as a source, and render it on an off-screen canvas, that you have also created, then read the content from the canvas
Done!
if the DOM node you want to render includes a <canvas> element with
something drawn on it, it should be handled fine, unless the canvas is
tainted -
in this case rendering will rather not succeed.
at the time of writing, Firefox has a problem with some external stylesheets (see issue #13). In such case, the error will be caught and logged.
Anatolii Saienko, Paul Bakaus (original idea)
MIT