Free tools Windows power users keep installed
One-click scans. No signup required.
If images disappear from an html2pdf.js PDF, the failure is usually upstream of PDF generation: html2pdf.js uses html2canvas to reconstruct the target DOM as a canvas, then passes that image to jsPDF. If the image never makes it onto the canvas, jsPDF has no pixels to put in the PDF. Start by checking image access and load state, then inspect the cloned DOM and canvas size.
How the image-to-PDF pipeline fails
html2pdf.js does not print the original page directly. It clones the selected content, asks html2canvas to render a DOM representation into a canvas, and gives that raster output to jsPDF. Missing images, blank output, or truncated pages therefore commonly originate during resource loading or canvas rendering—not in jsPDF itself.
This distinction gives you a useful first test: inspect or render the canvas before blaming PDF assembly. If the canvas already lacks an image, investigate the browser’s access to that resource, its readiness, or how the cloned DOM differs. If the canvas is correct but the PDF is not, then focus on the later PDF step.
Fix cross-origin images and tainted canvases
An image hosted on another origin is not automatically readable by JavaScript just because it appears in the browser. To include its pixels in a canvas export, the browser must approve access through CORS. By default, html2canvas skips cross-origin images when allowTaint is false. Its useCORS option asks the browser to load images with CORS, but the image server must also send an authorizing Access-Control-Allow-Origin response header.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems#1 Best Overall
Try this configuration when the image host permits your page’s origin:
html2pdf().set({
html2canvas: {
useCORS: true
}
}).from(element).save();
Setting useCORS: true cannot grant permission that the server has not provided. In DevTools, inspect the image request and response headers. If the server does not authorize the page’s origin, use an image proxy on an origin you control, or self-host the asset where licensing and access rules allow it. Configure the proxy option if your setup provides a suitable same-origin proxy:
html2pdf().set({
html2canvas: {
useCORS: true,
proxy: '/image-proxy'
}
}).from(element).save();
Do not treat allowTaint: true as an export workaround. It permits drawing an unapproved cross-origin resource in a way that taints the canvas; tainted canvases block pixel-reading and export operations such as getImageData(), toBlob(), and toDataURL() with a security exception. For a PDF pipeline that needs to export canvas pixels, the goal is a readable canvas, not a tainted one.
Rank #2
Wait for images before starting the capture
A capture can begin before an image finishes loading, especially when content is inserted asynchronously or the network is slow. Wait for the target’s image elements to either load or fail before calling html2pdf.js. The helper below resolves in either case so one broken image does not leave the whole operation waiting indefinitely; inspect failed images separately if they should prevent PDF creation.
async function waitForImages(element) {
const images = [...element.querySelectorAll('img')];
await Promise.all(images.map(img => {
if (img.complete) return Promise.resolve();
return new Promise(resolve => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
});
}));
}
async function savePdf(element) {
await waitForImages(element);
await html2pdf().set({
html2canvas: { imageTimeout: 30000 }
}).from(element).save();
}
html2canvas documents a default imageTimeout of 15,000 milliseconds; 0 disables the timeout. Increase the timeout only if slow resources are expected. A longer timeout does not repair a 404, a CORS rejection, or a resource that never becomes available. Where your installed version and call pattern support an onError callback, use it to log failed resource handling rather than silently treating every missing image as a timing issue.
If images are inserted after the initial page load, make sure your readiness check runs after that application code has populated the target. If fonts affect layout, wait for relevant fonts as well; a font swap can change dimensions even when all images are ready.
Check the cloned DOM, lazy loading, and visibility
html2pdf.js clones the node before rendering it, and its project documentation notes clone-node bugs as a limitation. The clone can differ from the live page in ways that hide or replace content. Lazy-loaded images may have their final URL in a data attribute rather than the cloned image’s src; animations or application state may leave the target hidden at capture time.
Use html2canvas‘s onclone callback to make temporary rendering-only changes. For example, transfer a known lazy-load attribute to src and disable animation in the clone. Adapt attribute names and selectors to your page rather than assuming every lazy-loading library uses the same markup:
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →html2pdf().set({
html2canvas: {
onclone: clonedDocument => {
clonedDocument.querySelectorAll('img[data-src]').forEach(img => {
img.src = img.dataset.src;
});
clonedDocument.querySelectorAll('.animated').forEach(node => {
node.style.animation = 'none';
node.style.visibility = 'visible';
});
}
}
}).from(element).save();
Keep these changes scoped to the clone so a PDF-specific fix does not unexpectedly alter the visible application. Confirm that the cloned target has usable dimensions and that the relevant elements are visible before treating the problem as an image-server issue.
Rank #4
Account for CSS, SVG, backgrounds, and iframes
html2canvas reconstructs a representation of the DOM; it is not a literal screenshot of the browser’s finished pixels. It supports a subset of CSS, so unsupported properties, pseudo-elements, filters, CSS background images, or combinations of SVG and external resources may render differently or disappear.
- For a fragile CSS background, test an ordinary
<img>element instead. - For SVG that relies on external resources or unsupported features, simplify it or use an image format and origin that the capture can read.
- Reduce the failing case to the smallest target element that still reproduces the problem; this separates a specific rendering feature from overall page complexity.
- Do not expect a cross-origin iframe’s contents to render: its
contentDocumentis inaccessible to the parent page.
When content lives inside an iframe you control, render the relevant content in an accessible document or capture it separately. Replacing inaccessible content with a supported same-origin element may be more dependable than trying to style around the browser’s origin boundary.
Recover from blank or cut-off PDFs
Very tall or wide captures can exceed the browser’s maximum canvas dimensions. The project documents maximum HTML5 canvas size as a failure mode: the result may be blank or stop partway through. This is a different problem from a single missing image, although both can appear as incomplete output.
Best Value
- Capture a smaller section or split a long document into multiple captures.
- Reduce the rendering scale if the canvas dimensions are too large.
- When the target’s intended scroll dimensions differ from the viewport, set
windowWidthandwindowHeightto the dimensions needed for rendering. - Check whether the canvas itself is blank or incomplete before changing jsPDF settings.
Splitting content changes pagination and may require you to manage page boundaries, but it avoids asking one canvas to represent an excessively large surface. Do not assume that a larger timeout will resolve a maximum-size failure.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Debug in an order that isolates the cause
- Open browser DevTools. Check Console and Network for CORS errors, 404 or 403 responses, mixed-content blocks, and image-decoding failures.
- Try one small same-origin PNG. If it renders, the basic html2pdf-to-canvas pipeline works; compare the failing resource’s origin, response, or format.
- Inspect the image response. For a cross-origin asset, look for an appropriate
Access-Control-Allow-Originheader. UseuseCORSonly when the server allows it; otherwise arrange a same-origin proxy or self-host the asset. - Verify readiness. Confirm target images have completed loading before capture, and adjust
imageTimeoutonly for plausibly slow resources. - Inspect clone state. In
onclone, address lazy-loaded sources, animations, hidden elements, and dimensions that differ from the live node. - Simplify the rendering target. Replace problematic backgrounds or SVG combinations and exclude inaccessible cross-origin iframe content.
- Reduce capture size. If the result is blank or truncated, try a smaller region, lower scale, or separate sections.
- Locate the failing stage. Check the generated canvas before PDF creation. A faulty canvas points to html2canvas or its inputs; a correct canvas moves the investigation downstream to PDF assembly.
Or skip the browser setup
If your real goal is to capture a public webpage by URL rather than export an arbitrary DOM element from your app, ScreenshotNeo can return a screenshot or PDF without you wiring up a browser-side html2pdf pipeline. It is not a drop-in fix for a client-rendered element that exists only inside your application; use the do-it-yourself steps above for that case.
For a URL capture, the cURL request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
See the ScreenshotNeo documentation for the API details. Cookie banners are accepted and removed before capture, along with known consent platforms, newsletter popups, and chat widgets; those cleanup steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies page verdict and billing status in headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents and MCP clients. The free plan includes 1,000 shots a month with no card; paid plans start at $5 for 3,000 shots.
Sign up for ScreenshotNeo and get 1,000 free screenshots a month with no card.
Frequently Asked Questions
Can html2pdf.js produce selectable text in the PDF?
Its described pipeline renders the target through html2canvas as a canvas image before passing that image to jsPDF, so the page content is rasterized rather than preserved as ordinary selectable document text.
Can ScreenshotNeo capture an element that exists only in my app’s DOM?
No. ScreenshotNeo accepts a URL for capture; it is an alternative for capturing a webpage by URL, not an exporter for an arbitrary in-memory DOM node.
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.

