SnapDOMGitHub★ 8K
Live demo

Live element

Component preview
Capture an editable component
1,284+12% vs last week

Editable text and an open shadow root can be captured together.

Result

Nothing captured yet

Edit the title, then run. This uses the real library from unpkg.
How-To Recipe

How to screenshot a div in JavaScript

Select the rendered element, call snapdom.toPng, and use the returned image in a preview or download.

TL;DR

const img = await snapdom.toPng(document.querySelector('#my-div')); — that's the entire recipe. The promise resolves to an HTMLImageElement you can append, download or upload.

Select and prepare the capture root

This recipe covers the element itself: selecting an attached, rendered div and capturing its current layout. To choose between a preview, a downloaded PNG and an upload-ready Blob, continue with the HTML to PNG output recipe.

Attach the div to the document and give it a measurable layout; display: none is not a renderable target. Wait for your application’s data and framework DOM update, then settle fonts and images in that element:

const el = document.querySelector('#card');
await document.fonts.ready;
await Promise.all(Array.from(el.querySelectorAll('img'), img => img.decode()));
const img = await snapdom.toPng(el);

If image decoding rejects, fix or replace the failed asset before retrying. For an embedded document or custom element, see the iframe and Shadow DOM recipes.

Step 1 — Install SnapDOM

npm install @zumer/snapdom@latest

If you don't use a bundler, you can also use the CDN build:

<script type="module">
  import { snapdom } from "https://unpkg.com/@zumer/snapdom@latest/dist/snapdom.mjs";
</script>

Step 2 — Pick the div you want to screenshot

Anything that's part of the live document tree works. Most people use document.querySelector:

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

The element doesn't need to be visible inside the viewport, but it does need to be attached to the document (not display: none).

Step 3 — Capture it

One line:

import { snapdom } from '@zumer/snapdom';

const img = await snapdom.toPng(el);

The returned img is a real HTMLImageElement with the captured content as its src (a PNG data URL).

Step 4 — Do something with the image

Append it to the page

const img = await snapdom.toPng(el);
document.body.appendChild(img);

Download it as a file

const result = await snapdom(el);
await result.download({ format: 'png', filename: 'card' });

Get a Blob to upload to a server

const blob = await snapdom.toBlob(el, { format: 'png' });

const form = new FormData();
form.append('file', blob, 'screenshot.png');
await fetch('/api/upload', { method: 'POST', body: form });

Full working example

Drop this into a fresh HTML file and open it in a browser:

<!DOCTYPE html>
<html>
<body>
  <div id="card" style="padding:24px;background:#6366f1;color:#fff;border-radius:12px;">
    <h2>Hello SnapDOM</h2>
    <p>This entire div is about to become a PNG.</p>
  </div>
  <button id="go">Capture</button>

  <script type="module">
    import { snapdom } from "https://unpkg.com/@zumer/snapdom@latest/dist/snapdom.mjs";

    document.getElementById('go').addEventListener('click', async () => {
      const img = await snapdom.toPng(document.getElementById('card'));
      document.body.appendChild(img);
    });
  </script>
</body>
</html>

Or try it right here

The example above, running on this page — same div, same snapdom.toPng call:

Hello SnapDOM

This entire div is about to become a PNG.

The captured PNG will appear here.

Useful options

  • dpr: 2 — pixel density (auto-detects window.devicePixelRatio for retina; override here when you need a fixed value).
  • scale: 2 — multiply the captured size (zoom). Different from dpr: scale makes the output bigger, dpr makes it sharper at the same logical size.
  • backgroundColor: '#fff' — fill transparent areas with a colour.
  • embedFonts: 'auto' — the v3 default embeds used web fonts.
  • exclude: ['.skip'] — skip any element matching the selector.
  • useProxy: 'https://your-proxy.example.com/' — fetch cross-origin images through a CORS proxy.

Frequently asked questions

Why not use the browser's built-in screenshot API?

There isn't one. The closest thing is the Screen Capture API (getDisplayMedia) which captures the whole tab or screen and requires user permission — it cannot target a specific DOM element. SnapDOM is the workaround.

Does this work with web fonts?

Yes. SnapDOM v3.x.x embeds used web fonts by default with embedFonts: 'auto', provided their files are accessible.

Can I screenshot something inside an iframe?

Same-origin iframes are supported out of the box. Cross-origin iframes are not — the browser blocks access to their content.

What about Shadow DOM?

SnapDOM walks open Shadow DOM trees during capture, so Web Components render correctly.

How big is the library?

SnapDOM has zero runtime dependencies. Check the distributed build you import for its current bundle size; official plugins are separate imports.

Try the live demo

See SnapDOM running in your browser before installing it.

Open the demo Installation

Choose the destination for your capture

Export the selected element as a searchable invoice PDF, reconstruct editable SVG and Figma artwork, or use the Claude Code capture skill to verify a UI change.