html2canvas does not save a file by itself. It returns a Promise that resolves to an HTMLCanvasElement. To store the result, export that canvas as a PNG data URL for a simple browser download, or create a Blob with toBlob() when the image is large or must be uploaded. The download happens entirely in the browser; no server is required for a local copy.
What html2canvas actually returns
Calling html2canvas(element) asks the browser to reconstruct the selected DOM element as a canvas. The call is asynchronous, so your saving code belongs in the Promise callback (or after await). The canvas is an in-memory object, not a file path and not persistent storage. Exporting it is a separate step.
html2canvas(document.querySelector('#invoice')).then(canvas => {
// Export the canvas here.
});
The library runs in the browser and can capture the whole document or a particular element. It is not a native browser screenshot: it traverses the DOM and draws a representation from properties it understands. Unsupported CSS, browser-rendered effects, and other implementation details can therefore look different from the live page.
Download a PNG with the simplest method
For a normal-sized image and a one-off local download, convert the canvas to a data URL, put it in an anchor, set the filename, and trigger a click.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minute#1 Best Overall
- Easily store and access 2TB to content on the go with the Seagate Portable Drive, a USB external hard drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
html2canvas(document.body).then(canvas => {
const link = document.createElement('a');
link.download = 'screenshot.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
A complete page example looks like this. Load html2canvas before the script, give the capture area an ID, and click the button after the page has rendered.
<button id="save-shot" type="button">Save screenshot</button>
<main id="capture">
<h1>Quarterly report</h1>
<p>This content will be rendered into the image.</p>
</main>
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
<script>
document.querySelector('#save-shot').addEventListener('click', async () => {
const element = document.querySelector('#capture');
const canvas = await html2canvas(element);
const link = document.createElement('a');
link.download = 'quarterly-report.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
</script>
The download attribute supplies the suggested name. The browser decides how the download is presented, so a user may still be asked where to save it or may have downloads routed to the browser’s configured folder.
Use toBlob() for large images and uploads
toDataURL() encodes the entire image into one in-memory string. That is convenient, but a large canvas can consume substantial memory and the resulting URL can be impractically long. toBlob() creates a binary Blob, which is a better handoff for a large download or an upload.
html2canvas(document.querySelector('#capture')).then(canvas => {
canvas.toBlob(blob => {
if (!blob) {
console.error('The browser could not encode the canvas.');
return;
}
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.download = 'screenshot.png';
link.href = url;
link.click();
// Release the temporary object URL after the browser has started the download.
setTimeout(() => URL.revokeObjectURL(url), 1000);
}, 'image/png');
});
The callback can receive null if encoding fails, so production code should check it. Revoke the object URL when it is no longer needed; otherwise repeated captures can leave temporary resources referenced in memory.
Choose an export format
| Format | Best use | Important behavior |
|---|---|---|
| PNG | Interfaces, text, diagrams, and transparency | The dependable default and required canvas export format. |
| JPEG | Photographic or very large images where a smaller file is useful | Lossy; a quality value from 0 to 1 can be supplied where the browser supports it. |
| WebP | Smaller modern image files when your target browsers support encoding | Support is browser-dependent; do not assume every browser can encode WebP. |
For JPEG, pass a quality value as the third argument to toBlob() or toDataURL():
Rank #2
- Easily store and access 1TB to content on the go with the Seagate Portable Drive, a USB external hard drive.Specific uses: Personal
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop. Reformatting may be required for Mac
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
canvas.toBlob(blob => {
if (blob) {
// use the JPEG Blob
}
}, 'image/jpeg', 0.85);
PNG is the safest choice when you do not control the browser or need crisp text. Changing formats does not fix a tainted canvas; cross-origin assets must be handled first.
Upload the screenshot or keep durable storage
A browser download is local and temporary from your application’s point of view. If users must retrieve the image later, send the Blob to a server or storage system selected by your application. The server endpoint, authentication, retention policy, and provider are architecture decisions; html2canvas only creates the image payload.
async function captureAndUpload() {
const canvas = await html2canvas(document.querySelector('#capture'));
const blob = await new Promise(resolve =>
canvas.toBlob(resolve, 'image/png')
);
if (!blob) throw new Error('Canvas encoding failed');
const form = new FormData();
form.append('screenshot', blob, 'capture.png');
const response = await fetch('/your-upload-endpoint', {
method: 'POST',
body: form
});
if (!response.ok) {
throw new Error(`Upload failed: ${response.status}`);
}
return response.json();
}
Replace /your-upload-endpoint with an endpoint you control. Do not put a secret storage credential in browser JavaScript. Validate the received MIME type and size on the server, and decide how long uploaded captures should remain available.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Capture the right element and wait for the page
Capture one region
Pass a specific element instead of document.body when the file should contain only a card, report, or component.
const element = document.querySelector('.receipt');
const canvas = await html2canvas(element);
Make sure fonts, images, and dynamic data have finished loading before calling html2canvas. If your page fills content after a network request, call the capture function after that request and after the relevant images are ready; otherwise the canvas can faithfully reproduce an incomplete page.
Rank #3
- High capacity in a small enclosure – The small, lightweight design offers up to 6TB* capacity, making WD Elements portable hard drives the ideal companion for consumers on the go.
- Plug-and-play expandability
- Vast capacities up to 6TB[1] to store your photos, videos, music, important documents and more
- SuperSpeed USB 3.2 Gen 1 (5Gbps)
Use a predictable filename
Build names from safe characters and a meaningful identifier, such as invoice-1042.png. Avoid putting unsanitized user input directly into a filename.
Cross-origin images, iframes, and blank exports
Cross-origin images can taint the canvas
Images loaded from another origin can make the canvas unsafe to read. A subsequent export may be blank or throw a SecurityError. html2canvas documents a useCORS option and a proxy option for resources that cannot be loaded same-origin.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Outdated 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 matchconst canvas = await html2canvas(document.querySelector('#capture'), {
useCORS: true
});
useCORS is useful only when the image server permits the browser’s cross-origin request. If it does not, route the resource through a proxy that you operate and configure html2canvas to use that proxy. A proxy must fetch the asset server-side and return it with the headers your page needs; simply turning on useCORS cannot grant access that the remote server has denied.
Cross-origin iframes are a hard boundary
html2canvas cannot render a cross-origin iframe because the page is not allowed to access that iframe’s contentDocument. You can capture the surrounding page, but the foreign document itself must be rendered by code running in its own origin or captured by a separate service.
Why the result differs from a browser screenshot
The library reconstructs the image from the DOM rather than asking the browser for a native bitmap. CSS or browser-rendered details that it does not understand may be missing or visually different. Treat the output as a DOM-based rendering, not a pixel-perfect replacement for a DevTools or operating-system screenshot.
Rank #4
- Plug-and-play expandability
- SuperSpeed USB 3.2 Gen 1 (5Gbps)
Performance and reliability checklist
- Prefer
toBlob()for large canvases. It avoids assembling the complete image in a giant data URL string. - Release object URLs. Call
URL.revokeObjectURL()after a download or upload no longer needs the URL. - Capture only what you need. A focused element uses less memory than an entire long document.
- Handle failures explicitly. Check for a null Blob, catch rejected Promises, and report upload HTTP errors.
- Do not assume every format works everywhere. PNG is the reliable baseline; JPEG and WebP encoding depend on browser support.
- Protect user data. A screenshot can contain private text or images. Treat downloaded files and uploaded Blobs as sensitive application data.
async function saveCapture() {
try {
const canvas = await html2canvas(document.querySelector('#capture'));
const blob = await new Promise(resolve =>
canvas.toBlob(resolve, 'image/png')
);
if (!blob) throw new Error('No image Blob was produced');
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'capture.png';
document.body.appendChild(link);
link.click();
link.remove();
setTimeout(() => URL.revokeObjectURL(url), 1000);
} catch (error) {
console.error('Screenshot failed', error);
}
}
Troubleshooting common failures
The downloaded file is blank
Check whether images came from another origin, whether a cross-origin iframe is inside the capture, and whether the capture ran before dynamic content loaded. Enable useCORS only when the image host allows it; otherwise provide a suitable proxy. Capture a smaller, same-origin element to isolate the offending asset.
The browser throws SecurityError
This usually indicates a tainted canvas caused by cross-origin content. Remove or proxy the foreign asset, or configure it for CORS. Exporting with a different format will not bypass the browser’s origin rules.
toBlob() returns null
Keep the null check and show a recoverable error. Retry after reducing the capture area or use PNG as the baseline format. If the canvas itself is invalid because of origin restrictions, fix that underlying problem first.
The image looks different from the page
That is expected when the page uses CSS or browser-rendered features outside html2canvas’s supported representation. Simplify the captured component, verify the DOM styles, or use a native browser capture service when pixel-level fidelity is a requirement.
The download works once but later captures consume memory
Repeated data URLs and unreleased object URLs keep large strings or Blob references alive. Use the Blob workflow, revoke each object URL, and avoid retaining old canvases in application state.
Best Value
- 【Upgraded version】 - The mirror logo strip is combined with the striped non-slip design. The rounded corners of the shell are more suitable for holding. The strips play a heat dissipation function to ensure a stable and fast transmission process.
- 【Ultra-thin and quiet】 - The motherboard adopts JMicron 578 noise-free solution, giving you a quiet working environment. Lightweight and portable size designed to fit in your pocket for easy portability.
- 【Ultra-Fast Data Transfers】 - Pairing this external hard drive with JMicron 578 solution USB 3.0 and USB 2.0 interfaces enables blazing-fast data transfer. It boasts theoretical read speeds of up to 125MB/s and write speeds of up to 103MB/s.
- 【Plug and Play】 - With no software to install, just plug it in and the drive is ready to use.The hard disk chip is wrapped with an aluminum anti-interference layer to increase heat dissipation and protect data.
- 【What You Get】 - 1 x Portable Hard Drive, 1 x USB 3.0 Cable, 1 x User Manual, Gift-type shell packaging ,Three-year manufacturer's warranty and free technical support services.
Or skip the browser setup
If you need a server-generated screenshot rather than a DOM canvas, ScreenshotNeo accepts one GET request and returns PNG, JPEG, WebP, or a PDF. It removes cookie-consent banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf.
See the ScreenshotNeo API documentation for all options. A direct 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
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 in 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 data = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', data);
Every feature is included on every plan. The Free plan 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 gives two months free. Create a free ScreenshotNeo account to start with 1,000 screenshots a month and no card.
Which storage method should you use?
| Need | Recommended approach |
|---|---|
| One normal-sized local download | toDataURL('image/png') plus an anchor with download. |
| Large image or repeated captures | toBlob(), an object URL, and explicit URL revocation. |
| Later retrieval, sharing, or processing | Upload the Blob to your own server or storage architecture. |
| Cross-origin pages, PDFs, or automated jobs | Use a browser-based screenshot service such as ScreenshotNeo instead of relying on a client canvas. |
The key distinction is that html2canvas creates the canvas; your export choice determines whether that canvas becomes a local file, a binary upload, or neither.
Recommended Free Tools
Frequently Asked Questions
Is a canvas data URL already stored as a file?
No. It is an encoded string held in memory. The browser creates a local file only when you place it on a downloadable link and trigger the download, or when your application sends the data to storage.
Can I reuse one Blob for both download and upload?
Yes. A Blob is a binary representation of the encoded canvas image, so application code can pass the same Blob to an object URL for download and to a multipart request, provided it has not discarded the reference.
Why does an iframe from another site disappear even when the rest of the page captures correctly?
A cross-origin iframe is not readable through its contentDocument. html2canvas cannot reconstruct that foreign document from the parent page; it must be rendered within its own origin or captured separately.
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.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →

