ScreenshotNeo

BlogHow-to

How to Load an HTML-to-Image JavaScript Library from a CDN

Load html-to-image safely from a pinned CDN URL, verify its browser export, and convert DOM elements to PNG, JPEG, SVG, or blobs.

By the ScreenshotNeo team1 October 20267 min read

To load html-to-image from a CDN, use a version-pinned browser bundle, place it before your application code, and verify the global export exposed by that exact file. The project documents npm and module imports, but does not promise one global name for every CDN build.

The example below uses the published 1.11.13 distribution path. Confirm that the file is still available and inspect its export when you publish or upgrade.

Quick start with a pinned CDN script

<!doctype html>
<html lang='en'>
<head>
  <meta charset='utf-8'>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
  <title>html-to-image CDN example</title>
  <style>
    #card { width: 360px; padding: 24px; background: #f4f7fb; border-radius: 12px; font: 16px system-ui; }
  </style>
</head>
<body>
  <div id='card'>
    <h1>Release notes</h1>
    <p>This DOM element will become a PNG.</p>
  </div>
  <button id='save'>Download PNG</button>

  <script src='https://cdn.jsdelivr.net/npm/html-to-image@1.11.13/dist/html-to-image.min.js'></script>
  <script>
    // Confirm the export name for the exact bundle you selected.
    const htmlToImage = window.htmlToImage;
    const card = document.getElementById('card');

    document.getElementById('save').addEventListener('click', async () => {
      try {
        if (!htmlToImage) throw new Error('html-to-image export was not found');
        const dataUrl = await htmlToImage.toPng(card);
        const link = document.createElement('a');
        link.download = 'release-notes.png';
        link.href = dataUrl;
        link.click();
      } catch (error) {
        console.error('Could not create the image:', error);
      }
    });
  </script>
</body>
</html>

The version-pinned file is listed by jsDelivr. The project README documents the rendering API and package imports at github.com/bubkoo/html-to-image. Check both before changing the version.

How the CDN loading sequence works

  1. Choose a browser build. A classic <script> tag needs a browser-ready distribution file, not the package’s npm import example.
  2. Pin the version. Use @1.11.13 (or a newer version you have reviewed) instead of an unpinned URL.
  3. Load it before dependent code. Put your script after the library tag, or wait for the script’s load event.
  4. Check the export. Open DevTools and inspect window after the script loads. Do not assume every CDN artifact exposes the same global.
  5. Capture after the DOM is ready. Call the API only after the target element and its styles, fonts, and images exist.

Loading with an explicit load event

<script>
  const script = document.createElement('script');
  script.src = 'https://cdn.jsdelivr.net/npm/html-to-image@1.11.13/dist/html-to-image.min.js';
  script.onload = () => {
    // Verify this name against the selected distribution file.
    const api = window.htmlToImage;
    if (!api) {
      console.error('The CDN file loaded, but its browser export has a different name.');
      return;
    }
    api.toPng(document.querySelector('#card'))
      .then((url) => { document.querySelector('#preview').src = url; })
      .catch(console.error);
  };
  script.onerror = () => console.error('The CDN script could not be loaded.');
  document.head.appendChild(script);
</script>

Convert a DOM element to each supported output

The documented API exposes Promise-based functions including toPng, toJpeg, toSvg, toBlob, toCanvas, and toPixelData.

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

const pngDataUrl = await htmlToImage.toPng(node);
const jpegDataUrl = await htmlToImage.toJpeg(node, { quality: 0. v });
const svgDataUrl = await htmlToImage.toSvg(node);
const blob = await htmlToImage.toBlob(node);
const canvas = await htmlToImage.toCanvas(node);
const pixels = await htmlToImage.toPixelData(node);

Replace the JPEG quality value with a number from 0 to 1, for example 0.9. Use a data URL for an <img> or download link, a Blob for uploads, a canvas for further drawing, SVG when you need serialized vector markup, and pixel data for image processing.

Using the package with a build tool

If your application already uses npm, the documented route is to install the package and import it. This avoids relying on a CDN global.

npm install html-to-image
import * as htmlToImage from 'html-to-image';

const node = document.getElementById('card');
const dataUrl = await htmlToImage.toPng(node);
document.getElementById('preview').src = dataUrl;

Named imports and CommonJS are also documented:

import { toPng } from 'html-to-image';
const dataUrl = await toPng(document.getElementById('card'));
const { toPng } = require('html-to-image');
const dataUrl = await toPng(document.getElementById('card'));

CDN choices and version pinning

Choice Use it when Check before shipping
jsDelivr You want the package distribution served from a CDN. Exact version and browser file path.
cdnjs Your team standardizes on cdnjs. That the requested version and bundle exist at cdnjs.
unpkg You need package files exposed directly. Whether the selected file is a browser bundle rather than source or an ES module.

Use a URL such as https://cdn.jsdelivr.net/npm/html-to-image@1.11.13/dist/html-to-image.min.js. An unpinned URL can change when a new package release becomes the default. If you add Subresource Integrity, generate or verify the hash against the exact bytes of the exact file and include crossorigin='anonymous' as required by your deployment.

Options that affect the rendered result

The library clones the target node, copies computed styles, embeds available fonts and images, serializes the result into SVG using <foreignObject>, and can draw that SVG to an off-screen canvas. This means the capture reflects the DOM and browser security context at capture time.

  • Wait until web fonts finish loading before calling the API.
  • Ensure images have loaded and can be fetched from the page’s origin or with suitable cross-origin permissions.
  • Capture a smaller subtree when the page is large; very large DOM trees can exceed data-URI limits.
  • Use toBlob instead of a large data URL when you plan to upload the result.
  • Keep the target’s dimensions explicit when consistent output size matters.

Common errors and fixes

Symptom Likely cause Fix
htmlToImage is undefined The script has not loaded, the code ran first, or the bundle uses another export name. Use a load event, move your code after the CDN tag, and inspect the exact bundle’s global in DevTools.
Blank or incomplete image The target was captured before fonts, images, or dynamic content finished loading. Wait for document.fonts.ready, image load events, and application rendering before capture.
Security or tainted-canvas error A cross-origin image or font was not permitted or embedded. Serve assets with appropriate CORS headers, host them on the same origin, or remove the inaccessible asset.
Missing styles Styles are applied by state, pseudo-elements, or resources unavailable to the clone. Set the state before capture, verify computed styles, and make required resources reachable.
Huge output fails The cloned DOM or generated data URI is too large. Capture a smaller element, reduce dimensions, or use toBlob.
Internet Explorer fails The project requires SVG foreignObject, which Internet Explorer does not support. Use a browser with the required SVG support or a server-side screenshot workflow.

Reliability and performance checklist

  • Pin the CDN version and record the URL in source control.
  • Fail clearly when the library or target node is missing.
  • Wrap every Promise in try/catch or attach .catch().
  • Capture only the required element instead of the entire document.
  • Reuse a loaded script rather than injecting it for every capture.
  • Test fonts, images, SVG content, dark mode, and responsive dimensions in each supported browser.
  • Do not treat historical browser versions listed in the README as current compatibility guarantees.

Or skip the browser setup

If you need a finished website screenshot rather than a DOM export inside a user’s browser, ScreenshotNeo provides a single HTTP request for PNG, JPEG, WebP, or PDF output. Its cleanup step accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

cURL:

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

Python:

import requests

r = requests.get(
    'https://api.screenshotneo.com/v1/shot',
    params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'},
    timeout=90,
)
r.raise_for_status()
open('shot.webp', 'wb').write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', image);

See the ScreenshotNeo API documentation for request options. It also offers an MCP server so Claude, Cursor, and other MCP clients can take screenshots; 1,000 screenshots per month are free with no card, and paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account and start with 1,000 screenshots each month at no charge.

FAQ

Can I use the npm import directly in a classic script tag?

No. The README’s import examples are module or bundler usage. Select a browser bundle and verify its actual export.

Does toPng return a file?

It returns a Promise fulfilled with a data URL. Convert that URL to a download, or use toBlob when an upload-friendly Blob is better.

Why do remote images disappear?

The browser may block them because of cross-origin rules, or the image may not have loaded before capture. Fix the asset’s CORS access or serve it from an allowed origin, then wait for loading.

Should I use a CDN or npm?

Use a CDN for a small standalone page or demonstration. Use npm when you have a build pipeline, want imports checked during builds, or need dependency updates managed with the rest of your application.

Is version 1.11.13 permanent?

No. Package releases and CDN availability can change. Recheck the release history and package listing before updating this URL.