For an in-page download, select the element and pass it to html2canvas, then export the returned canvas as a PNG. This recreates the element from its DOM and styles, so it is not a pixel-for-pixel capture of the browser. For automated, browser-rendered capture, use Playwright’s element screenshot API instead.
Choose the capture method first
The right implementation depends on where the screenshot is produced and how faithful it must be.
| Requirement | Best fit | What you receive |
|---|---|---|
| A button in your web app downloads a card, receipt or chart | html2canvas |
A canvas in the user’s browser, which you can export to PNG |
| Visual regression, testing or server-side automation | Playwright | A screenshot file or buffer of the browser-rendered element |
| A remote URL must be captured without running your own browser | ScreenshotNeo | PNG, JPEG, WebP or PDF from one HTTP request |
These approaches are not interchangeable. html2canvas traverses the DOM and redraws properties it understands. Playwright captures the element as rendered by a real browser. Neither approach bypasses browser origin or content-security rules.
Build an in-page div download with html2canvas
1. Install or import the library
Add html2canvas to your JavaScript application using the package manager and module system used by your project, then import it:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problems#1 Best Overall
import html2canvas from 'html2canvas';
If your page uses a bundler, put the import in the entry module. The important part is that the library is loaded before the capture function runs.
2. Create a target element
<section id='capture' class='receipt'>
<h2>Order complete</h2>
<p>Order #1842 · 2 items</p>
<strong>$48.00</strong>
</section>
<button id='download' type='button'>Download receipt</button>
The selector must identify an element that exists when the handler executes. If the section is rendered conditionally, attach the handler after it has been inserted or call the function after the relevant render completes.
3. Capture and download the PNG
import html2canvas from 'html2canvas';
async function downloadDiv(selector, filename = 'div.png') {
const element = document.querySelector(selector);
if (!element) {
throw new Error(`No element found for selector: ${selector}`);
}
const canvas = await html2canvas(element);
const dataUrl = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = filename;
link.href = dataUrl;
link.click();
}
document.querySelector('#download').addEventListener('click', async () => {
try {
await downloadDiv('#capture', 'receipt.png');
} catch (error) {
console.error('Could not capture the div', error);
alert('The receipt could not be captured. Check the console for details.');
}
});
The essential call is const canvas = await html2canvas(element). The returned Promise resolves to a canvas. Calling toDataURL('image/png') creates an image URL; assigning it to an anchor’s download property and clicking that anchor starts the file download.
Use a Blob when the image is large
A data URL keeps the entire encoded image in a string. For larger regions, a Blob download avoids that extra string copy:
function saveCanvas(canvas, filename) {
canvas.toBlob((blob) => {
if (!blob) {
throw new Error('The browser could not encode the canvas');
}
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}, 'image/png');
}
Call saveCanvas(canvas, 'receipt.png') immediately after the capture. If your application needs to support browsers where the download click must occur during the user gesture, keep the capture and click in the same button-driven flow.
Rank #2
Control the captured region and resolution
html2canvas can capture the selected element as a whole or crop a region with the documented coordinate and size options. The coordinates are relative to the page capture area.
| Option | Use it for | Trade-off |
|---|---|---|
x, y |
Starting crop coordinates | A crop that starts away from the element’s top-left corner |
width, height |
Limiting the output rectangle | Content outside the rectangle is omitted |
scale |
Increasing or reducing output pixel density | Higher values create more pixels and use more memory |
useCORS |
Attempting to load cross-origin images when their server permits CORS | It cannot override a server’s headers or browser security policy |
proxy |
Routing eligible remote resources through a proxy | The proxy must be configured correctly; it is not a way around access controls |
const canvas = await html2canvas(element, {
x: 0,
y: 0,
width: element.scrollWidth,
height: element.scrollHeight,
scale: 2,
useCORS: true
});
Use a scale that matches the intended display or print size. Doubling scale in both dimensions produces roughly four times as many pixels, so very large cards can consume substantial memory. Start with the default, then increase it only when the output needs more detail.
Know what html2canvas can and cannot reproduce
html2canvas does not take the browser’s actual pixels. It reads the DOM and CSS properties it supports and builds a representation on a canvas. Unsupported CSS may therefore render differently from the live page: a visual effect that looks correct in the browser can be missing, simplified or positioned differently in the export.
Cross-origin images
Browser origin rules are the most common source of failures. An image hosted on another origin must be served with suitable CORS headers if the canvas is to read it. useCORS only asks the browser to make a CORS-enabled request; it does not grant permission. A proxy is another documented option, but it also must be under your control and correctly configured.
Cross-origin iframes and existing canvases
An iframe from another origin cannot be traversed by your page’s script. Likewise, an image or canvas that has already tainted the canvas can make pixel export unreadable. If toDataURL() throws a security error, identify the resource that crossed the origin boundary rather than trying to disable browser protections.
Wait for the content you want
Capture only after fonts, data and images needed by the target have been rendered. For an element created by a framework, run the function from the event or lifecycle point that follows insertion. For content that changes after a network response, wait for that response and the resulting DOM update before calling html2canvas.
Capture a rendered element with Playwright
For tests, visual regression jobs and server-driven automation, Playwright’s locator screenshot method captures the element in a real browser rather than reconstructing it in a client-side canvas.
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 →import { chromium } from 'playwright';
const targetUrl = process.env.TARGET_URL;
if (!targetUrl) throw new Error('Set TARGET_URL before running');
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1280, height: 800 }
});
await page.goto(targetUrl, { waitUntil: 'networkidle' });
await page.locator('.card').screenshot({ path: 'card.png' });
await browser.close();
Replace .card with the selector for the element to capture. The resulting card.png is written by Playwright. This is a better fit when the capture runs outside the page or must reflect browser rendering, but it requires a browser process and an automation environment.
html2canvas versus Playwright
| Axis | html2canvas | Playwright |
|---|---|---|
| Runtime | Inside the interactive page | Automated browser process |
| Rendering model | DOM and supported-style reconstruction | Browser-rendered element screenshot |
| Output | Canvas, data URL or Blob that your code downloads | Screenshot file or buffer |
| Best use | A user-facing “Download” button | Regression tests, scheduled jobs and capture services |
| Origin constraints | Browser CORS, iframe and tainted-canvas rules apply | The page still runs under browser security rules |
Troubleshooting common failures
“No element found” or a null selector
Cause: the selector is misspelled, the element has not rendered, or the code ran before the document was ready. Fix: inspect document.querySelector(selector), use the exact ID or class, and invoke capture after the component mounts.
The output is blank or missing late content
Cause: capture started before asynchronous data, images or layout changes finished. Fix: wait for the data promise and the DOM update, then call html2canvas. For automated capture, wait for the page state that your application uses before taking the Playwright screenshot.
Rank #4
Images or an iframe disappear
Cause: the resource is cross-origin and cannot be read, or the iframe document is inaccessible. Fix: serve images with appropriate CORS headers, try useCORS only when the server supports it, or configure a legitimate proxy. You cannot use either option to bypass browser policy.
toDataURL reports a security error
Cause: the canvas became tainted by an inaccessible image or another canvas. Fix: remove or replace that resource, correct its CORS response, or omit it from the capture.
CSS looks different from the page
Cause: html2canvas does not implement every CSS property and redraws only what it understands. Fix: simplify the target’s styling for export, provide an export-specific class, or use Playwright when browser-rendered fidelity is more important than an in-page download.
The tab freezes or the image is enormous
Cause: a large element combined with a high scale creates a very large bitmap. Fix: capture only the needed rectangle, reduce scale, and avoid repeatedly capturing on every keystroke or scroll event. Capture on demand and release any temporary object URLs after a Blob download.
Performance and reliability checklist
- Keep the target as small as the user requirement allows.
- Choose
scalefrom the final display or print size, not from the device’s maximum pixel ratio. - Wait for dynamic content before capture and handle the returned Promise with
try/catch. - Test representative fonts, images, filters and layout states because unsupported CSS can change the result.
- Test every remote asset under the actual production origin; a same-origin development setup can hide CORS failures.
- For repeated or unattended jobs, prefer a browser automation workflow and log the target URL, selector and failure reason.
Or skip the browser setup
ScreenshotNeo is the first API alternative to try when a remote page must be captured without maintaining Playwright infrastructure: it removes consent banners, newsletter popups and chat widgets before the shot, and bills only clean shots.
Free tools Windows power users keep installed
One-click scans. No signup required.
One GET request returns a PNG, JPEG, WebP or PDF. Replace the example URL with the page you need.
Best Value
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}`);
See the ScreenshotNeo documentation for request details. Options include full-page capture with lazy images loaded, one-element CSS-selector capture, dark mode, 12 device presets or any viewport, retina scale, PDF paper size and page ranges, HTML/CSS-to-image, custom CSS and JavaScript, pre-capture clicks, hidden selectors, waits for a selector, delay or network idle, blocking ads, trackers, requests or resource types, custom headers, cookies, user agent and Authorization, timezone and geolocation, transparent backgrounds, image resizing, a chosen cache TTL, signed links for public <img> tags, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify a migration.
Bot checks and CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed. Each response identifies the page verdict and billing result in X-Page-Verdict and X-Billed headers. ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to begin.
Frequently Asked Questions
Can I keep familiar parameter names when moving to ScreenshotNeo?
Yes. ScreenshotNeo accepts the parameter names used by other screenshot APIs, which can reduce changes in an existing integration.
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 →Are ScreenshotNeo features restricted to higher plans?
No. Every feature is available on every plan; yearly billing provides two months free.
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.

