How-To Recipe
zumerlab/snapdom

How to convert HTML to PNG in JavaScript

Three patterns: append the image to the page, download it as a file, or get a Blob to upload. Pick the one that fits your use case and copy the snippet.

TL;DR

const img = await snapdom.toPng(element); — that returns an HTMLImageElement. Append it, download it, or convert to a Blob with snapdom.toBlob(element) if you need to upload.

Step 1 — Install

npm install @zumer/snapdom

Or load it from a CDN if you don't use a bundler:

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

Pattern 1 — Append the PNG to the page

Useful for quick previews and demos. snapdom.toPng returns an HTMLImageElement, ready to insert anywhere in the DOM:

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

const img = await snapdom.toPng(document.querySelector('#card'));
document.body.appendChild(img);

Pattern 2 — Download the PNG as a file

SnapDOM ships with a one-liner that triggers the browser's download flow:

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

If you need more control, build the download yourself with a Blob and an anchor tag:

const blob = await snapdom.toBlob(document.querySelector('#card'));
const url = URL.createObjectURL(blob);

const a = document.createElement('a');
a.href = url;
a.download = 'card.png';
a.click();

URL.revokeObjectURL(url);

Pattern 3 — Upload the PNG to a server

Capture the element to a Blob, wrap it in FormData, and POST it to your endpoint:

const blob = await snapdom.toBlob(document.querySelector('#card'));

const form = new FormData();
form.append('file', blob, 'card.png');

const response = await fetch('/api/upload', {
  method: 'POST',
  body: form,
});

const { url } = await response.json();
console.log('Uploaded to', url);

This is the same pattern you'd use to generate dynamic og:image share cards — capture the rendered card client-side, upload to a CDN, return the URL.

Bonus — Get a Base64 / data URL

If you specifically need a base64 string (e.g. to email or embed inline), call toPng and read the src of the returned image:

const img = await snapdom.toPng(document.querySelector('#card'));
const dataUrl = img.src; // "data:image/png;base64,iVBORw0KGgo…"

High-DPI / retina output

Pass the scale option to render at higher resolution. scale: 2 doubles the pixel dimensions of the output PNG, which is what you want for retina displays:

const img = await snapdom.toPng(el, { scale: 2 });

Combine with backgroundColor to fill transparent areas:

const img = await snapdom.toPng(el, {
  scale: 2,
  backgroundColor: '#ffffff',
});

Frequently asked questions

Can I convert HTML to PNG without a server?

Yes. SnapDOM runs entirely in the browser. There is no server required and no network round-trip — the conversion uses SVG foreignObject and the browser's own renderer.

What if I need JPG or WebP instead of PNG?

Use snapdom.toJpg(el, { quality: 0.9 }) or snapdom.toWebp(el, { quality: 0.9 }). The API surface is identical, only the output format changes.

How do I render custom fonts correctly?

Pass { embedFonts: true }. SnapDOM will inline the matching @font-face declarations into the SVG so the captured image renders with your real fonts instead of falling back to system ones.

Can I exclude some elements from the capture?

Yes. Pass { exclude: ['.skip-me'] } with a list of CSS selectors, or { filter: node => … } for fine-grained control.

What about converting HTML to PDF?

Use the pdfImage plugin. It adds a toPdfImage() method to the result object that wraps the captured image in a single-page PDF. Browse the plugins page for details.

Does it handle Shadow DOM and Web Components?

Yes. SnapDOM walks open Shadow DOM trees during cloning, so Web Components render the same way they do in the live page.

Try the live demo

Capture any element on snapdom.dev to see the output before installing.

Open the demo Install from npm