Free tools Windows power users keep installed
One-click scans. No signup required.
Set backgroundColor: '#ffffff' when calling html2canvas(). That gives the exported canvas an opaque white backdrop. Do not use backgroundColor: null: null preserves transparency. If the transparent area belongs to a particular element, change that element only in html2canvas’s cloned document with onclone, leaving the live page untouched.
Set a white canvas background
The simplest capture is:
const element = document.querySelector('#invoice');
html2canvas(element, {
backgroundColor: '#ffffff'
}).then((canvas) => {
const link = document.createElement('a');
link.download = 'invoice.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
backgroundColor controls the canvas backdrop used during rendering. A hexadecimal white value such as #ffffff is opaque, so pixels that would otherwise reveal the canvas’s transparent backdrop appear white in the PNG or other export.
The distinction between these two values is important:
| Setting | Result | Use it when |
|---|---|---|
'#ffffff' |
Paints an opaque white canvas background. | You need a white page, card, or document export. |
null |
Keeps the canvas transparent. | You intentionally need alpha transparency for compositing. |
If you are seeing transparent corners or empty space, check that you have not copied an example that sets backgroundColor: null. That value is the opposite of a white export.
#1 Best Overall
When the element’s own background is transparent
The option above paints the canvas backdrop. It does not rewrite every transparent CSS declaration in the element being captured. For a transparent region that must become white, use onclone. html2canvas gives you a cloned document for the render; style the clone there so the application UI is not changed:
const source = document.querySelector('#report');
html2canvas(source, {
backgroundColor: '#ffffff',
onclone: (clonedDoc) => {
clonedDoc.querySelectorAll('.transparent-region').forEach((node) => {
node.style.backgroundColor = '#ffffff';
});
}
}).then((canvas) => {
document.querySelector('#preview').replaceChildren(canvas);
});
Use a selector that identifies only the areas requiring a fill. Styling every node in the clone can hide intentional transparency, such as icons or overlays. You can also add a temporary class to a wrapper in onclone and define the white fill with a stylesheet rule, or capture a wrapper that already has a white background:
html2canvas(document.querySelector('#report'), {
backgroundColor: '#ffffff',
onclone: (clonedDoc) => {
const report = clonedDoc.querySelector('#report');
report.classList.add('capture-on-white');
}
});
.capture-on-white {
background-color: #ffffff !important;
}
The class is added to the cloned tree, not the live tree. Remove any capture-only rule from your normal stylesheet if it could affect the live page through another selector.
Complete reusable capture functions
White backdrop for the whole capture
Wrap the option in a function when many buttons or routes need the same behavior:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →async function downloadWhiteScreenshot(selector, filename) {
const target = document.querySelector(selector);
if (!target) {
throw new Error(`No element matches ${selector}`);
}
const canvas = await html2canvas(target, {
backgroundColor: '#ffffff'
});
const anchor = document.createElement('a');
anchor.download = filename;
anchor.href = canvas.toDataURL('image/png');
anchor.click();
}
downloadWhiteScreenshot('#dashboard', 'dashboard.png');
The promise resolves with a canvas. You can append that canvas, call toDataURL(), or convert it to a Blob for an upload. The white backdrop is applied during rendering rather than painted onto the finished bitmap.
Rank #2
White only selected transparent regions
async function captureReport() {
const target = document.querySelector('#report');
const canvas = await html2canvas(target, {
backgroundColor: '#ffffff',
onclone(clonedDoc) {
clonedDoc.querySelectorAll('[data-white-in-capture]').forEach((node) => {
node.style.backgroundColor = '#ffffff';
});
}
});
const blob = await new Promise((resolve, reject) => {
canvas.toBlob((value) => value ? resolve(value) : reject(new Error('PNG encoding failed')), 'image/png');
});
return blob;
}
A data attribute makes the intent explicit and avoids coupling capture code to presentational class names. If no matching nodes exist, only the canvas backdrop changes.
Why transparent pixels do not automatically look white
A fully transparent color has an alpha value of zero. Its red, green, and blue components are not visible until an opaque layer is placed behind it. Canvas bitmaps use premultiplied-alpha semantics, so RGB values stored in a transparent pixel cannot be relied on as a visible white color. An opaque white canvas background supplies that missing layer. This is why changing an element’s nominal transparent color to an RGB value alone does not guarantee a white export.
There are two separate layers to reason about:
- Canvas backdrop: controlled by
backgroundColor; it affects the entire rendered bitmap. - Element paint: controlled by the cloned DOM’s styles; it affects only the selectors you change in
oncloneor a white wrapper.
Choose the first when every empty area should be white. Choose the second when only particular cards, panels, or regions should lose transparency.
Comparing the available approaches
| Approach | Live DOM modified? | Scope | Transparency preserved? | CSS-support dependency |
|---|---|---|---|---|
backgroundColor: '#ffffff' |
No | Entire canvas | No, the backdrop is opaque | Low; it is a renderer option |
backgroundColor: null |
No | Entire canvas | Yes | Low; it intentionally keeps alpha |
onclone selector override |
No; styles are changed in the clone | Selected elements | Only where you do not override the background | Depends on html2canvas implementing the relevant CSS |
| White wrapper or capture-only class | No when applied in the clone | A wrapper and its descendants | Only outside the white wrapper | Depends on the wrapper properties being supported |
CSS fidelity still matters
A white backdrop cannot make html2canvas support CSS features that it does not implement. The project FAQ notes that every CSS property must be implemented manually and that full CSS support is not a goal. Unsupported effects can therefore differ from the browser view even when the background color is correct. Check shadows, filters, blend modes, masks, complex gradients, and other advanced styling if the result looks different.
For the most predictable export, use a capture-specific class with straightforward backgrounds and dimensions, wait until fonts and images have loaded, and compare the rendered canvas with the source at the same viewport size. If a property is not reproduced, replace it in the cloned document with a simpler equivalent rather than changing the production UI.
Rank #3
Cross-origin images are a separate failure mode
Images loaded from another origin can taint the canvas. When that happens, reading the result with toDataURL() or toBlob() may fail even though the page appeared correctly in the browser. The html2canvas FAQ identifies CORS handling as the remedy: serve the image with appropriate cross-origin headers and configure the capture accordingly. A white backgroundColor does not bypass browser canvas security.
- Prefer same-origin image URLs when you control the assets.
- For a separate image host, make sure it sends an
Access-Control-Allow-Originresponse that permits your page. - Test the export path, not just the visual preview; canvas readability is what determines whether encoding succeeds.
Troubleshooting transparent-to-white captures
The output is still transparent
Inspect the actual options object passed to html2canvas(). A later spread operation or helper may be replacing '#ffffff' with null. Log the final options, remove the null assignment, and ensure you are exporting the canvas returned by that call rather than a different canvas.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →The page background is white, but a card remains transparent
The card’s own background is transparent. Keep backgroundColor: '#ffffff' for the canvas and add an onclone rule targeted at that card, or put the card inside a wrapper that receives a white capture-only class.
The live page flashes white
Your code is probably changing the real element before capture. Move the style change into onclone, which receives the cloned document, or style a separate off-screen capture wrapper. Avoid toggling the production class around an asynchronous call because users can see intermediate frames and other scripts can observe the temporary state.
Only some transparent regions change
Check selector scope and specificity. querySelectorAll() operates on the cloned document, so a selector that matches the live document must also exist in the cloned subtree. Inline styles normally win over ordinary stylesheet rules; use a narrowly scoped !important only when an existing rule prevents the white fill.
Rank #4
The screenshot looks different from the browser
Investigate unsupported CSS first. Simplify the affected effect in onclone and capture again. A correct white backdrop does not correct layout, font, filter, or compositing differences.
toDataURL() or toBlob() throws a security error
Look for cross-origin images or other resources. Configure CORS on the asset host, use same-origin copies, and retry. This problem is independent of whether the canvas background is white or transparent.
The capture runs before content appears
Call html2canvas only after the target’s asynchronous content, images, and fonts are ready. For dynamic components, wait for the component’s own ready signal or a specific DOM condition before invoking the renderer. Otherwise the clone faithfully captures an incomplete state.
Performance and reliability practices
- Capture the smallest useful element instead of the entire document when a component image is all you need.
- Reuse one capture helper so every route applies the same background and clone rules.
- Do not mutate the live DOM merely to obtain a white export; clone-only changes avoid visual flicker and make concurrent captures safer.
- Release large canvases and object URLs after an upload or download, especially when users generate several high-resolution images.
- Keep a transparent export path when downstream compositing needs alpha; use a separate white-export option rather than permanently changing the component’s design.
- Test at the viewport and device-pixel settings used in production. Different dimensions can change wrapping and therefore the pixels that appear transparent.
Or skip the browser setup
If you need a URL rendered on a server instead of a browser DOM, ScreenshotNeo is a website screenshot API and MCP server. It accepts a URL and returns PNG, JPEG, WebP, or PDF. Cookie and consent banners are accepted and removed before capture, along with more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
Here is a one-call WebP capture; see the ScreenshotNeo API documentation for the complete option list:
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,
)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
ScreenshotNeo also provides full-page captures with lazy images loaded, CSS-selector element captures, dark mode, device presets or custom viewports, retina scale, PDF controls, custom CSS and JavaScript, click and wait actions, request and resource blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, which can simplify migration. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
Best Value
The Free plan includes 1,000 shots each month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to try the capture service.
Final checklist
- Use
backgroundColor: '#ffffff'for an opaque white export. - Use
backgroundColor: nullonly when transparency is required. - Use
oncloneor a clone-only white wrapper for selected transparent elements. - Keep cross-origin images CORS-readable before calling
toDataURL()ortoBlob(). - Account for CSS properties html2canvas does not implement and test the actual export path.
Frequently Asked Questions
Can I offer both transparent and white downloads from the same component?
Yes. Keep the component’s normal styles unchanged and expose two capture functions: one passing backgroundColor: null, and one passing backgroundColor: '#ffffff' with any needed onclone overrides.
Does a white canvas background fix a tainted canvas?
No. Canvas tainting caused by cross-origin images is a browser security issue. Configure CORS or use same-origin assets before reading the rendered canvas.
Recommended Free Tools
Where should capture-only white styles live?
Put them in the onclone callback or on a wrapper class added to the cloned document. That keeps the user’s live page unchanged.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

