ScreenshotNeo

BlogHow-to

Why HTML-to-PNG Images Aren’t Transparent and How to Fix Them

HTML-to-PNG output is often white because the renderer or CSS paints a background. Learn how to preserve alpha transparency and fix common capture failures.

By the ScreenshotNeo team1 October 20267 min read

HTML-to-PNG output is usually opaque for one of three reasons: the renderer fills the canvas with white, your HTML or an ancestor paints a background, or the output format cannot preserve alpha. With html2canvas, set backgroundColor: null, remove opaque backgrounds from the captured element and its ancestors, and export as PNG.

A transparent canvas cannot erase pixels that your page intentionally painted. If you use another renderer or a hosted HTML-to-image service, use that product’s documented transparency option and verify its format rules.

What causes a white background?

Cause What you see Fix
Renderer default The page has no visible background, but the PNG is white For html2canvas, use backgroundColor: null
CSS background The captured card, wrapper, body, or html is painted Remove or override the relevant background or background-color
Wrong format Transparency disappears after conversion or download Use PNG and preserve the alpha channel
Different capture tool Options from html2canvas have no effect Follow the selected tool’s own transparency settings

The html2canvas configuration documentation lists #ffffff as the default canvas background when the DOM does not specify one, and null as the transparent setting. The library reconstructs an image from DOM and style information; it is not the same as a browser’s native screenshot.

Fix html2canvas transparency step by step

1. Capture with a null background

<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
<script>
  const element = document.querySelector("#card");

  const canvas = await html2canvas(element, {
    backgroundColor: null
  });

  const blob = await new Promise((resolve) =>
    canvas.toBlob(resolve, "image/png")
  );

  if (!blob) {
    throw new Error("The browser could not create a PNG blob");
  }

  const link = document.createElement("a");
  link.download = "card-transparent.png";
  link.href = URL.createObjectURL(blob);
  link.click();
  URL.revokeObjectURL(link.href);
</script>

This changes the canvas fill. It does not make a white card, wrapper, or page background transparent.

2. Remove painted backgrounds from the capture tree

html, body {
  background: transparent;
}

#capture-shell,
#card {
  background-color: transparent;
}

/* Keep visible backgrounds only on elements that should remain opaque. */
#card .content-panel {
  background: #172033;
}

Inspect the target element, every wrapper around it, and the html and body rules. A background image, gradient, pseudo-element, box shadow, or absolutely positioned layer can also create apparently opaque pixels.

3. Export and save as PNG

PNG is the appropriate format when the alpha channel matters. Avoid converting the canvas to JPEG. If a service documents that its JPEG and WebP responses are white-backed, request PNG instead; do not assume every encoder treats WebP the same way.

A complete browser example

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Transparent HTML to PNG</title>
  <style>
    html, body {
      margin: 0;
      background: transparent;
      font-family: system-ui, sans-serif;
    }

    #card {
      display: inline-block;
      padding: 32px;
      color: white;
      border-radius: 20px;
      background: linear-gradient(135deg, #6d5dfc, #23c6b8);
    }
  </style>
</head>
<body>
  <div id="card">This gradient remains visible; the outside stays transparent.</div>
  <button id="download">Download PNG</button>

  <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
  <script>
    document.querySelector("#download").addEventListener("click", async () => {
      const element = document.querySelector("#card");
      const canvas = await html2canvas(element, {
        backgroundColor: null,
        scale: window.devicePixelRatio
      });

      const blob = await new Promise(resolve =>
        canvas.toBlob(resolve, "image/png")
      );
      if (!blob) throw new Error("PNG encoding failed");

      const url = URL.createObjectURL(blob);
      const link = document.createElement("a");
      link.href = url;
      link.download = "card.png";
      link.click();
      URL.revokeObjectURL(url);
    });
  </script>
</body>
</html>

The gradient is intentionally opaque inside #card. The transparency applies to pixels outside that painted shape.

When html2canvas output differs from the browser

According to the html2canvas documentation, the library traverses the DOM and styles to reconstruct an image and supports only the CSS properties it implements. A transparency problem and a fidelity problem can therefore occur at the same time.

  • Reduce the example to one element and one background rule.
  • Check unsupported or partially supported CSS, pseudo-elements, filters, blend modes, and complex clipping.
  • Wait for fonts, images, and layout changes before calling html2canvas.
  • Use the browser’s native screenshot tooling or a browser-based service when pixel fidelity matters more than running entirely in the page.

Cross-origin images and tainted canvases

Images loaded from another origin can trigger browser canvas security restrictions. The html2canvas FAQ explains that cross-origin resources need suitable CORS response headers or a proxy. If they are unavailable, the image may disappear or reading/exporting the canvas may fail.

const canvas = await html2canvas(element, {
  backgroundColor: null,
  useCORS: true
});

useCORS only helps when the remote server sends an appropriate CORS header. It cannot override the browser’s same-origin policy. A proxy must fetch the resource and return it in a way the browser can use.

Hosted rendering options

A hosted HTML/CSS-to-image service can render outside the visitor’s browser and expose a documented transparent-background option. One documented workflow uses transparent_background: true; another sets body { background-color: transparent; }. Check the service’s format rules as well: the cited documentation supports transparency for PNG and describes white backgrounds for JPG and WebP.

Choose a browser-side library when the page is already in the browser and you need no server. Choose hosted rendering when you need repeatable server-side captures, URL-based input, or a pipeline that should not depend on a user’s browser permissions.

Or skip the browser setup

ScreenshotNeo is a website screenshot API with a documented transparent-background option, plus full-page, element, CSS, JavaScript, viewport, device, wait, blocking, cookie, header, and caching controls. See the ScreenshotNeo API documentation for the available parameters.

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)
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}`);

Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. ScreenshotNeo also provides an MCP server so Claude, Cursor, and other MCP clients can take screenshots. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.

Create a free ScreenshotNeo account and start with the included 1,000 screenshots.

Troubleshooting checklist

Symptom Likely cause Fix
Entire image is white Default canvas background or an opaque page ancestor Set backgroundColor: null; inspect html, body, wrappers, and pseudo-elements
Only the card is white The card itself has background or background-color Remove it or keep it only on the portion that should be opaque
Transparency worked in canvas but not in the file JPEG conversion or an encoder that drops alpha Call toBlob(..., "image/png") and preserve the PNG file
Remote images are missing CORS or browser content-policy restrictions Enable server CORS headers or use a proxy; useCORS alone cannot bypass policy
toDataURL or export throws a security error The canvas is tainted by a cross-origin resource Fix CORS for every image, font, or other resource included in the render
Output does not match the page Unsupported CSS or DOM reconstruction differences Simplify the markup, check supported properties, and use a browser renderer when exact fidelity is required
Background appears after an animation Capture happened before styles or fonts settled Wait for fonts, images, and layout updates before capturing
Hosted API still returns white The service has its own option or format rule Enable its documented transparent setting, set page CSS to transparent, and request PNG

Performance, reliability, and cost considerations

  • Keep the capture region small. Capturing one element uses less memory than rendering a long document. Remove off-screen content when it is not needed.
  • Control pixel dimensions. A high device-pixel scale improves sharpness but increases canvas memory, encoding time, and file size.
  • Wait deliberately. Capture after fonts, images, and dynamic layout have settled. A fixed delay is simple; waiting for a specific selector is usually more deterministic.
  • Cache stable results. For repeated server captures, cache by URL and relevant rendering options. Invalidate when content or styles change.
  • Retry selectively. Retry transient network failures, but do not repeatedly retry deterministic CORS, authentication, or unsupported-CSS errors.
  • Measure the final file. Verify the PNG color type and alpha channel in your image pipeline; some post-processing tools flatten transparency.
  • Budget by successful output. With ScreenshotNeo, clean shots are billed while bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. The response headers report the verdict and billing status.

FAQ

Does backgroundColor: null make every white pixel transparent?

No. It makes the canvas background transparent. White pixels painted by your HTML, CSS, images, or pseudo-elements remain white.

Can JPEG contain transparent pixels?

No. Use PNG when the alpha channel is required.

Is html2canvas a native browser screenshot?

No. It reconstructs an image from DOM and style data, so unsupported CSS and browser content restrictions can change the result.

Why does a transparent PNG look white in my viewer?

Some viewers display transparency against a white checkerboard or white canvas. Place the PNG over a contrasting background or inspect its alpha channel in an image editor.

Should I use a browser library or an API?

Use a browser library when the content already exists in the page and client-side capture is acceptable. Use a hosted API when you need repeatable server-side URL captures, controlled rendering options, or an automated workflow.