ScreenshotNeo

BlogHow-to

How to Take Screenshots with dom-to-image

Capture a DOM element as PNG, JPEG, SVG, a downloadable Blob, or pixel data with dom-to-image, including options, failures, and browser limits.

By the ScreenshotNeo team1 October 20268 min read

Direct answer: install dom-to-image, select the DOM node you want to capture, and call one of its promise-based methods: toPng, toJpeg, toSvg, toBlob, or toPixelData. The method returns a promise containing an image data URL, Blob, SVG data, or RGBA pixel bytes.

The library clones the node, copies computed styles, recreates pseudo-elements, embeds fonts and images, serializes the result into SVG with <foreignObject>, and rasterizes that SVG through an off-screen canvas for PNG, JPEG, and pixel output. That means external resources, browser security rules, and support for <foreignObject> directly affect the result. The original project README is the authoritative API reference: dom-to-image README.

1. Install and capture an element

Install the original package from npm:

npm install dom-to-image

With ES modules:

import domtoimage from 'dom-to-image';

const node = document.getElementById('my-node');

domtoimage.toPng(node)
  .then((dataUrl) => {
    const image = new Image();
    image.src = dataUrl;
    document.body.appendChild(image);
  })
  .catch((error) => {
    console.error('Screenshot failed', error);
  });

With CommonJS:

const domtoimage = require('dom-to-image');

const node = document.getElementById('my-node');
domtoimage.toPng(node)
  .then((dataUrl) => console.log(dataUrl))
  .catch((error) => console.error('Screenshot failed', error));

The node must exist when the call runs. Wait until the page has rendered its content, fonts, and images before capturing.

2. Complete browser example with download buttons

This self-contained example captures one card as PNG, JPEG, SVG, a Blob, or raw pixel data. It uses the browser build produced by your bundler; import the package in your application code as shown above.

import domtoimage from 'dom-to-image';

const node = document.querySelector('#card');

function downloadDataUrl(dataUrl, filename) {
  const link = document.createElement('a');
  link.download = filename;
  link.href = dataUrl;
  link.click();
}

document.querySelector('#png').addEventListener('click', async () => {
  try {
    const dataUrl = await domtoimage.toPng(node);
    downloadDataUrl(dataUrl, 'card.png');
  } catch (error) {
    console.error(error);
  }
});

document.querySelector('#jpeg').addEventListener('click', async () => {
  try {
    const dataUrl = await domtoimage.toJpeg(node, { quality: 0.95 });
    downloadDataUrl(dataUrl, 'card.jpg');
  } catch (error) {
    console.error(error);
  }
});

document.querySelector('#svg').addEventListener('click', async () => {
  try {
    const dataUrl = await domtoimage.toSvg(node);
    downloadDataUrl(dataUrl, 'card.svg');
  } catch (error) {
    console.error(error);
  }
});

document.querySelector('#blob').addEventListener('click', async () => {
  try {
    const blob = await domtoimage.toBlob(node);
    const objectUrl = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.download = 'card.png';
    link.href = objectUrl;
    link.click();
    URL.revokeObjectURL(objectUrl);
  } catch (error) {
    console.error(error);
  }
});

document.querySelector('#pixels').addEventListener('click', async () => {
  try {
    const rgba = await domtoimage.toPixelData(node);
    console.log('RGBA byte length:', rgba.length);
    console.log('First pixel:', rgba.slice(0, 4));
  } catch (error) {
    console.error(error);
  }
});

Use a Blob when a download helper or another file API expects a Blob. Use pixel data when your code needs RGBA values rather than an image file.

3. Choose the output method

Method Returns Use it for
toPng(node, options) PNG data URL Lossless raster output, previews, and normal downloads
toJpeg(node, options) JPEG data URL Compressed images where adjustable quality is acceptable
toSvg(node, options) SVG data URL Keeping the serialized SVG representation
toBlob(node, options) Image Blob File downloads and APIs that accept Blob objects
toPixelData(node, options) Uint8Array of RGBA components Programmatic pixel inspection

The README documents these formats but does not publish comparative speed or size benchmarks. Select the format from your actual consumer’s needs and measure your own page if performance matters.

4. Configure the documented options

Option What it does Important behavior
filter Includes or excludes nodes during cloning. Returning false excludes that node and its children. The root node is not passed to the filter.
bgcolor Sets a background color for the rendered output. Useful when the source element is transparent and the output needs a solid background.
width, height Overrides the rendered dimensions. Set both deliberately when you need a fixed output size.
style Copies style properties onto the node before rendering. Use it for temporary capture-only adjustments such as padding or overflow.
quality Controls JPEG quality from 0 to 1. The documented default is 1.0. It affects JPEG output, not PNG.
cacheBust Adds the current time as a query parameter to resource requests. The documented default is false. It can help avoid stale fetched resources, but changes request URLs.
imagePlaceholder Provides a data URL to use when an image cannot be fetched. Without a placeholder, image-fetch failures reject the promise by default.

Filtering an element

const options = {
  filter: (child) => !child.classList.contains('do-not-capture')
};

const dataUrl = await domtoimage.toPng(
  document.querySelector('#card'),
  options
);

Applying capture-only styles

const dataUrl = await domtoimage.toPng(
  document.querySelector('#card'),
  {
    bgcolor: '#ffffff',
    width: 900,
    height: 500,
    style: {
      padding: '24px',
      overflow: 'visible'
    }
  }
);

Providing an image placeholder

const transparentPixel =
  'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';

const dataUrl = await domtoimage.toPng(node, {
  imagePlaceholder: transparentPixel
});

5. Capture only the content you need

Select the smallest stable element that contains the intended result. A chart, invoice card, or product panel is usually a better target than the whole document because it reduces cloning work and avoids unrelated navigation or overlays.

const chart = document.querySelector('[data-chart="revenue"]');
const chartPng = await domtoimage.toPng(chart, {
  bgcolor: '#fff',
  style: { margin: '0' }
});

For content that changes after an interaction, perform the interaction first, then wait for the application state and images to settle. The library captures the DOM state at the time of the call; it does not provide a page-navigation or network-idle controller.

6. Why resources and browser security affect results

During cloning, dom-to-image attempts to embed web fonts and image resources and copies computed styles and pseudo-elements. External stylesheets, remote images, embedded canvases, and font loading can therefore change the output or cause rejection.

  • Cross-origin images: images that cannot be fetched or embedded may make the promise reject. Configure the server for appropriate cross-origin access, or provide imagePlaceholder.
  • Tainted canvases: drawing cross-origin content into a canvas can taint it. Reading or exporting that canvas then fails under browser security rules.
  • External stylesheets: the original README records a Firefox issue involving some external stylesheets. Inline or otherwise make required styles available to the capture context when practical.
  • Fonts: wait for web fonts before capture so the cloned styles resolve to the intended typeface.
  • Missing images: the default behavior is to throw when an image cannot be fetched; use a placeholder if a partial image is preferable.
await document.fonts.ready;

const node = document.querySelector('#report');
const dataUrl = await domtoimage.toPng(node).catch((error) => {
  console.error('Could not render report:', error);
  throw error;
});

7. Browser compatibility and maintenance caveats

The original README says the project was tested on Chrome 49 and Firefox 45, excludes Internet Explorer because it lacks SVG <foreignObject> support, and calls Safari unsupported because of stricter security around that feature. Those are historical statements from the project documentation, not current compatibility guarantees.

The npm listing identifies version 2.6.0 and says it was last published nine years ago. Treat that maintenance and browser information as stale until you verify the package in every browser your application supports. Run captures against your real fonts, images, stylesheets, and canvases before relying on the result in production.

8. Troubleshooting

Symptom Likely cause Fix
Cannot read properties of null or an empty capture The selector ran before the node existed or selected the wrong element. Run after rendering, check the selector, and log the node before calling dom-to-image.
Promise rejects while loading an image An image URL could not be fetched or embedded. Fix the resource URL and cross-origin response, wait for loading, or set imagePlaceholder.
Canvas export fails or content is missing A cross-origin resource tainted a canvas. Keep canvas resources same-origin or configure the resource server for the required cross-origin access.
Fonts fall back Web fonts were not ready when cloning occurred, or the font resource was inaccessible. Await document.fonts.ready, then capture; verify the font request and embedding path.
Styles are missing An external stylesheet was inaccessible or affected by browser foreignObject rules. Make the required styles available to the page, test the target browser, and use capture-only style overrides for small corrections.
JPEG is too large or visibly degraded The quality setting does not match the use case. Adjust quality between 0 and 1 and compare the resulting files for your content.
Output is clipped The source element has constrained dimensions or overflow. Set explicit width/height and, when appropriate, capture-only style such as overflow: visible.
Safari or Internet Explorer does not work The original project documentation excludes them for foreignObject security or support reasons. Verify current behavior yourself and provide another capture path for unsupported browsers.

9. Performance, reliability, and cost considerations

  • Capture a focused node instead of the entire document when that meets the requirement.
  • Avoid calling captures repeatedly during every animation frame. Capture after the state changes you actually need.
  • Large DOM trees, many images, web fonts, and SVG or canvas content increase the amount of cloning and resource embedding work.
  • Use toBlob when your next step is a file upload or download, instead of converting a large data URL unnecessarily.
  • Handle every returned promise with catch or try/catch; resource and browser-security failures are expected failure modes for this architecture.
  • There is no published benchmark in the original README or npm listing. Measure capture time, output size, and failure rate on your own pages and supported browsers.
  • The npm package itself is a browser-side library. Your application pays the browser’s normal compute and bandwidth costs; the dossier identifies no separate dom-to-image service pricing.

10. Or skip the browser setup

If you need a URL screenshot from a server or automation job, ScreenshotNeo provides a GET request that returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for the request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server gives AI agents tools named take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000 shots.

Create a free ScreenshotNeo account and start with 1,000 screenshots a month without adding a card.

11. FAQ

Can dom-to-image capture an entire web page?

It captures the DOM node you pass. Select a page container and set explicit dimensions if that container represents the full content you need; the library does not navigate to a URL or automatically manage page scrolling.

Does toPixelData return RGB values?

It returns a Uint8Array with RGBA components, so each pixel uses four byte values.

Should I use the original package or dom-to-image-more?

This guide covers the original tsayen/dom-to-image project. The similarly named dom-to-image-more is a separate fork with its own documentation and should not be assumed to have the same options or behavior.

Is there a current browser support matrix?

The original documentation provides historical statements rather than a current matrix. Reproduce captures in the browsers and page configurations your application promises to support.

When is a remote screenshot API a better fit?

Use a remote API when the input is a URL and you want a consistent capture service without installing and maintaining browser-side capture code, especially for automation, scheduled jobs, and AI-agent workflows.

Sources