The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Wait for every image that matters to finish loading and decoding before you call html2canvas. Use HTMLImageElement.decode() when available, reject or deliberately handle failures, then await the promise returned by html2canvas. Do not treat img.complete alone as proof of success: it is also true for broken images and images with no source.
The reliable capture sequence
A deterministic capture has three separate waits:
- Make required lazy images eligible to load (for example, scroll them into view).
- Wait until each target image has loaded and decoded successfully.
- Call
html2canvasand await its rendering promise before exporting the canvas.
The readiness check must cover the same content you intend to capture. If your target includes content inserted later, run the check after that DOM change and immediately before the capture.
A production-ready JavaScript helper
This helper prefers decode(), validates already-complete images with naturalWidth, and falls back to load/error events in older environments.
Recommended Free Tools
async function waitForImages(root) {
const images = [...root.querySelectorAll("img")];
await Promise.all(images.map(async (img) => {
// complete can also mean broken or source-less, so validate naturalWidth.
if (img.complete && img.naturalWidth > 0) {
if (typeof img.decode === "function") {
await img.decode();
}
return;
}
// decode() waits for a usable decoded image and rejects on failure.
if (typeof img.decode === "function") {
await img.decode();
return;
}
// Older-browser fallback.
await new Promise((resolve, reject) => {
img.addEventListener("load", resolve, { once: true });
img.addEventListener(
"error",
() => reject(new Error(`Image failed: ${img.currentSrc || img.src}`)),
{ once: true }
);
});
if (img.naturalWidth === 0) {
throw new Error(`Image is not usable: ${img.currentSrc || img.src}`);
}
}));
}
async function capture(element) {
await waitForImages(element);
const canvas = await html2canvas(element, {
imageTimeout: 15000
});
return canvas;
}
const element = document.querySelector("#report");
if (!element) throw new Error("Capture target #report was not found");
try {
const canvas = await capture(element);
const pngUrl = canvas.toDataURL("image/png");
document.querySelector("#preview").src = pngUrl;
} catch (error) {
console.error("Capture failed", error);
}
The documented imageTimeout default is 15,000 milliseconds; setting it to 0 disables that timeout. A timeout is only a limit on waiting, not a guarantee that an image loaded successfully. Check the configuration for the html2canvas version installed in your project because options can vary by release.
#1 Best Overall
Choose what “ready” means for your page
Waiting for every image is safest, but not always necessary. Define the scope and failure policy explicitly.
| Decision | Strict capture | Best-effort capture |
|---|---|---|
| Scope | All img descendants of the target, plus any externally inserted content you include |
Only images essential to the result; optional thumbnails may be skipped |
| Failure | Reject immediately and report the failed URL | Log the failure and continue, or replace the image with a known fallback |
| Use case | Invoices, archival screenshots, compliance evidence | Dashboards or feeds where one optional image should not block the whole view |
The sample code uses the strict policy. To continue after an optional failure, wrap each image wait in a try/catch, record the URL, and resolve only for images your application marks as nonessential. Do not silently hide failures when pixel accuracy matters.
Why common shortcuts fail
window.onload and DOMContentLoaded
These events describe document lifecycle milestones, not the readiness of images added later by JavaScript. They also do not guarantee that lazy images outside the viewport have even started a request.
Free tools Windows power users keep installed
One-click scans. No signup required.
A fixed delay
setTimeout(...) guesses at network and decode time. It can be unnecessarily slow on a fast connection and still too short on a slow one. An image-specific promise gives you a meaningful condition instead.
img.complete by itself
MDN documents that complete is true when the image has finished fetching, but also when it is broken or has no source. Pair it with naturalWidth > 0, and prefer decode() for usable decoded pixels.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Calling html2canvas without awaiting it
html2canvas returns a promise. Exporting or reading the canvas before that promise resolves can produce incomplete output or race with rendering. Always use const canvas = await html2canvas(element, options).
Lazy-loaded images: start the request first
An image with loading="lazy" may not request its resource until it approaches the viewport. Before waiting, make required images eligible. The simplest browser-side approach is to scroll the target into view and allow the browser a frame to schedule loading:
element.scrollIntoView({ block: "center" });
await new Promise(requestAnimationFrame);
await waitForImages(element);
For a long page, you may need to scroll through the capture region or temporarily change the page’s loading strategy. Re-run the check after any code that changes src, srcset, image visibility, or the target DOM.
Cross-origin images and canvas security
Loading and decoding are separate from permission to use pixels. A cross-origin image can load successfully yet be omitted by html2canvas or taint the canvas, preventing operations such as toDataURL(). The remote server must allow the request through CORS, or you must use a configured proxy.
Set useCORS: true only when the image server sends an appropriate CORS response. The proxy option is another documented route for fetching resources through a same-origin service. allowTaint: true is not an export fix: an origin-tainted canvas remains unreadable. Configure the server or proxy instead.
Rank #3
const canvas = await html2canvas(element, {
useCORS: true,
imageTimeout: 15000
});
CORS headers must be present on the image response, not merely on your HTML page. If credentials are involved, the server’s credential and origin headers must also match your request mode.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsDebugging checklist
Find the actual images
const images = [...element.querySelectorAll("img")];
console.table(images.map(img => ({
src: img.currentSrc || img.src,
complete: img.complete,
naturalWidth: img.naturalWidth,
naturalHeight: img.naturalHeight,
loading: img.loading
})));
If the screenshot includes a portal, shadow-root content, or a node outside the selected subtree, include that content in your readiness logic or capture a containing element.
Decode rejects
A rejected decode() normally means the resource is missing, corrupted, unsupported, or no longer available. Log currentSrc, verify the URL in the browser network panel, and decide whether to fail, omit, or substitute it.
The image is present but blank
Check naturalWidth, CSS visibility, responsive srcset selection, and whether a lazy-loading trigger occurred. A successful decode does not override CSS that hides the element.
The canvas export throws a security error
This is usually an origin-taint problem, not a timing problem. Confirm CORS response headers or route the image through a properly configured proxy. Waiting longer cannot change browser security rules.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchRank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
The result differs from the browser
html2canvas reconstructs a representation from DOM and supported CSS; it does not capture the browser’s actual pixels. Unsupported CSS, filters, fonts, canvas size limits, and layout changes can affect fidelity independently of image readiness.
Performance and reliability practices
- Limit scope: query only the images inside the capture target instead of scanning the whole document.
- Wait in parallel:
Promise.allavoids serial network waits while still enforcing an all-images policy. - Use a timeout policy: retain html2canvas’s 15-second image timeout unless your application has a justified alternative; disabling it with
0can leave a capture waiting indefinitely. - Capture once the DOM is stable: stop animations or mutation-driven updates that can replace image sources after your readiness check.
- Record failures: include the URL and error type in logs so operators can distinguish a missing asset from CORS or rendering limitations.
- Test the installed version: options and browser behavior can differ across html2canvas releases.
Alternative implementations
Event-only helper
When decode() is unavailable, wait for either load or error, then verify dimensions:
function waitForLoadOrError(img) {
if (img.complete) {
return img.naturalWidth > 0
? Promise.resolve()
: Promise.reject(new Error("Image is broken or empty"));
}
return new Promise((resolve, reject) => {
img.addEventListener("load", () => {
if (img.naturalWidth > 0) resolve();
else reject(new Error("Image loaded with no usable pixels"));
}, { once: true });
img.addEventListener("error", () => reject(new Error(img.src)), { once: true });
});
}
Filtering essential images
const required = [...element.querySelectorAll("img[data-required]")];
await Promise.all(required.map(img => img.decode()));
Use a deliberate marker such as data-required; do not rely on incidental class names that may change with a redesign.
Or skip the browser setup
For server-side or repeatable captures, ScreenshotNeo provides a website screenshot API and MCP server. One request returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status.
Read the ScreenshotNeo API documentation for all options. A minimal call is:
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://stripe.com
-o shot.webp
The same request in 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)
And 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(`HTTP ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', buffer);
ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. Every feature is included on every plan; 1,000 screenshots per month are free with no card, and paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Best Value
FAQ
Should I wait for fonts as well as images?
If text metrics affect the layout, wait for document.fonts.ready separately; image readiness does not guarantee stable font rendering.
Can I capture a failed image as an empty box?
Yes. Adopt a best-effort policy, replace the source with a controlled placeholder, and resolve the wait only after that replacement is decoded.
Does waiting improve unsupported CSS?
No. Readiness prevents missing image resources; it cannot add CSS features that html2canvas does not implement.
Frequently Asked Questions
Should I wait for fonts as well as images?
If text metrics affect the layout, wait for document.fonts.ready separately; image readiness does not guarantee stable font rendering.
Can I capture a failed image as an empty box?
Yes. Use a best-effort policy, replace it with a controlled placeholder, and wait for that replacement to decode.
Does waiting fix unsupported CSS?
No. It addresses image readiness only; html2canvas still has its own CSS and canvas fidelity limits.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →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.

