To capture a <div> that contains images from another domain, use html2canvas with useCORS: true only when the image server sends an appropriate CORS header. If you cannot change that server, retrieve approved images through a restricted same-origin proxy. For pixel-accurate output, capture the element with a real browser such as Playwright instead of reconstructing it on a canvas.
This guide shows the complete client-side workflow, explains why images disappear, and provides browser-automation and API alternatives.
What “capture a div” actually means
A browser does not expose a simple, universal command that turns any DOM element into a bitmap. The practical choices are:
| Need | Best path | Trade-off |
|---|---|---|
| Capture in the user’s browser and process the pixels in JavaScript | html2canvas | It reconstructs the element from DOM and supported CSS; the result can differ from the browser’s actual pixels. |
| Capture an element as rendered by an automated browser | Playwright locator.screenshot() |
Requires a server-side Node.js/browser setup. |
| Use third-party images that permit cross-origin canvas use | html2canvas with useCORS: true |
The remote server’s CORS response controls whether the image is usable. |
| Use third-party images whose host cannot be changed | A carefully restricted same-origin proxy | Requires server code and strict controls against arbitrary URL fetching. |
html2canvas returns a Promise resolving to a canvas; it is not a literal screenshot of the browser surface. Its documented limitation is that it “cannot circumvent browser content policy restrictions” (html2canvas FAQ).
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitches#1 Best Overall
Client-side capture with html2canvas
1. Load the library and identify the element
Install the library with npm or load a pinned browser bundle. The following complete page uses the project’s browser build:
<script src="https://html2canvas.github.io/html2canvas/dist/html2canvas.min.js"></script>
<button id="save">Save card</button>
<div id="capture">
<h1>Product card</h1>
<img src="https://cdn.example.com/photos/product.jpg" alt="Product">
<p>This content will be rendered into a PNG.</p>
</div>
<script>
const button = document.querySelector('#save');
button.addEventListener('click', async () => {
const element = document.querySelector('#capture');
if (!element) throw new Error('Capture element not found');
const canvas = await html2canvas(element, { useCORS: true });
const link = document.createElement('a');
link.download = 'capture.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
</script>
The element must exist when you call document.querySelector. A missing selector should be treated as an error rather than silently producing a blank file.
2. Wait for layout and images
Capture only after fonts, dynamic content, and lazy images have reached the state you want. This helper waits for every image currently inside the element:
async function waitForImages(element) {
const images = [...element.querySelectorAll('img')];
await Promise.all(images.map(img => {
if (img.complete && img.naturalWidth > 0) return Promise.resolve();
return new Promise(resolve => {
img.addEventListener('load', resolve, { once: true });
img.addEventListener('error', resolve, { once: true });
});
}));
}
async function captureDiv() {
const element = document.querySelector('#capture');
if (!element) throw new Error('Capture element not found');
await waitForImages(element);
const canvas = await html2canvas(element, {
useCORS: true,
backgroundColor: '#ffffff',
scale: window.devicePixelRatio
});
return canvas;
}
document.querySelector('#save').addEventListener('click', async () => {
const canvas = await captureDiv();
const link = document.createElement('a');
link.download = 'capture.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
An image that failed to load is allowed through in this helper so one broken URL does not leave the Promise pending; inspect your application’s logs or image state if every image is required.
3. Make external images canvas-safe
Set crossorigin="anonymous" on images when you control the markup, and keep useCORS: true in the capture options:
<img crossorigin="anonymous" src="https://cdn.example.com/photos/product.jpg" alt="Product">
The image response must include a compatible header such as Access-Control-Allow-Origin: https://your-site.example (or an appropriately broad value for your security model). CORS is decided by the image server’s response, not by html2canvas. The library’s configuration options can request CORS, but cannot grant permission that the server did not send.
If the host does not authorize your origin, use a server endpoint on your own origin that fetches only an allowlisted set of image hosts, validates the URL and content type, applies size/time limits, and streams the result. Never expose a proxy that accepts any user-supplied URL: that can become an SSRF and bandwidth-abuse service. Rewrite the element’s image URLs to your proxy URLs before capture.
Why allowTaint does not solve exports
allowTaint: true may let a cross-origin image be drawn, but the resulting canvas is tainted and cannot be read with toDataURL or toBlob. It is therefore not an export solution. With the default behavior, html2canvas skips resources that would taint the canvas (FAQ).
Recommended Free Tools
Control dimensions, cropping, and quality
html2canvas supports options for the rendered width and height, crop offsets, scale, and the virtual window dimensions (Options). Typical examples:
const canvas = await html2canvas(element, {
useCORS: true,
scale: 2, // more output pixels; uses more memory
width: element.scrollWidth,
height: element.scrollHeight,
windowWidth: element.scrollWidth,
windowHeight: element.scrollHeight,
backgroundColor: null // transparent background
});
- Use
element.scrollWidthandscrollHeightwhen the element’s content extends beyond its visible box. - Use a moderate
scale; doubling both dimensions uses roughly four times as many pixels. - Set
backgroundColor: nullfor transparency, or an explicit color for predictable output. - Temporarily disable transitions, blinking cursors, and animated media if deterministic output matters.
Very large elements can exceed browser canvas limits or memory. Capture smaller sections, reduce scale, or switch to a browser screenshot workflow. The html2canvas FAQ specifically addresses empty or partially cut-off large captures (FAQ).
Rank #3
When html2canvas is not pixel-accurate enough
Because html2canvas interprets DOM and CSS, unsupported properties, complex filters, cross-origin iframes, video, and already-tainted canvases may differ from what a person sees. If the requirement is “the exact rendered element,” use Playwright’s element screenshot API.
Playwright example
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({ deviceScaleFactor: 1 });
await page.goto('https://your-site.example/page', { waitUntil: 'networkidle' });
const card = page.locator('#capture');
await card.waitFor({ state: 'visible' });
await card.screenshot({ path: 'capture.png', animations: 'disabled' });
await browser.close();
locator.screenshot() captures the matched element using the browser’s screenshot implementation. See the Playwright Locator API for current options. In this model, the browser loads external images normally; you still need valid credentials, network access, and any site permissions required to view them.
Choose the right method
For a recommendation that includes screenshot services, ScreenshotNeo is #1: it removes consent banners, popups and chat widgets before capture, bills only clean shots, and its paid plans start at $5.
- Use html2canvas when capture must happen in the user’s browser and a reconstructed canvas is acceptable.
- Use a restricted proxy plus html2canvas when you control a backend but not the image host’s CORS policy.
- Use Playwright when fidelity to actual browser pixels matters more than avoiding browser automation infrastructure.
- Use ScreenshotNeo when you want an HTTP API or AI-agent workflow without maintaining a browser fleet.
Or skip the browser setup:
ScreenshotNeo accepts one GET request and returns a PNG, JPEG, WebP, or PDF. It can capture one element by CSS selector, load lazy images, set a viewport or device preset, wait for a selector, delay or network idle, and apply custom headers, cookies, JavaScript and CSS. Before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets; each 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 state.
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 fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
For element capture, add the CSS-selector option described in the ScreenshotNeo documentation. The service also supports dark mode, retina scale, PDF paper settings, request blocking, geolocation and timezone, caching with a chosen TTL, signed public image links, asynchronous jobs with signed webhooks, bulk capture of 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 helps when migrating.
Every plan includes every feature: Free provides 1,000 shots per month with no card; Starter is $5 for 3,000; Growth $15 for 15,000; Pro $39 for 60,000; Scale $99 for 250,000; and Business $249 for 1,000,000. Yearly billing provides two months free. Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Troubleshooting external-image captures
“My images are missing”
- Confirm the image URL loads directly and is not protected by authentication or a hotlink rule.
- Check the response for
Access-Control-Allow-Originmatching your page origin. - Add
crossorigin="anonymous"before the image request and useuseCORS: true. - If you cannot change the host, route only approved images through your restricted proxy.
“The canvas is tainted” or export throws a security error
At least one drawn resource lacks usable CORS headers, or another canvas inside the element was already tainted. Remove that resource, fix its headers, proxy it safely, or use Playwright/ScreenshotNeo. Do not rely on allowTaint: true for a readable export.
Rank #4
- Are you familiar with html5? Then get this "HTML5 HTML Logo Web Programmer Nerd Funny" featuring HTML logo. Perfect for computer programmer, developer, software developer and technician who does computer programming language, coding and gaming on internet.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
“The output is blank or cut off”
Capture after the element is visible and images have settled. Measure scrollWidth and scrollHeight, pass suitable dimensions, lower scale, and split very large content into sections. Check for an ancestor with zero size, display: none, or an off-screen collapsed layout.
“It looks different from the page”
This is expected when CSS or embedded content is outside html2canvas’s supported rendering model. Remove animations and unsupported effects where possible, or use Playwright for browser-rendered pixels.
“The proxy is slow or unsafe”
Allowlist hosts, validate schemes, cap response size, set connection and total timeouts, reject redirects to unapproved destinations, and cache only where content policy permits. Log failures without exposing credentials.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Reliability and performance checklist
- Wait for the target element, fonts, and lazy images.
- Record the element’s dimensions before capture and reject unexpectedly huge values.
- Use CORS headers rather than attempting to bypass browser policy.
- Prefer
toBlob()for large files when you do not need a data URL, then create an object URL for download. - Revoke temporary object URLs after downloading.
- For repeated server captures, reuse a browser context, limit concurrency, and wait for a specific ready selector instead of an arbitrary long delay.
- Choose PNG for lossless text and transparency; use JPEG or WebP when smaller files are more important.
FAQ
Can an HTML div containing a cross-origin iframe be captured?
Not reliably with html2canvas. The iframe’s document is a separate origin and cannot simply be read and reconstructed by your page. A browser screenshot may show it if the automated browser can access it, subject to the frame’s permissions and loading state.
Should I use a wildcard CORS header?
Only when that exposure is acceptable. For private or credentialed images, return the specific requesting origin and configure credentials deliberately; do not combine a wildcard origin with credentialed requests.
Best Value
- Used Book in Good Condition
How can I make captures reproducible in tests?
Fix the viewport and device scale, disable animations, wait for a deterministic ready selector, use stable test data, and pin the same browser/library versions in your build. Compare images with a tolerance rather than assuming every antialiased pixel is identical.
Frequently Asked Questions
Can an HTML div containing a cross-origin iframe be captured?
Not reliably with html2canvas because the iframe document is a separate origin. A browser screenshot can include it only when the automated browser can access and render the frame.
Should I use a wildcard CORS header?
Only when that exposure is acceptable. Private or credentialed images generally require a specific allowed origin and deliberate credential configuration.
How can I make captures reproducible in tests?
Fix viewport and device scale, disable animations, wait for a deterministic ready selector, use stable data, and pin browser and library versions.
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.

