Free tools Windows power users keep installed
One-click scans. No signup required.
Capture the modal after it is visible, not the whole page: select its root element, call html2canvas(modal), then export the returned canvas with toBlob() or toDataURL(). The browser library reconstructs the selected DOM and CSS; it does not make a native browser screenshot. That distinction explains most differences in fonts, filters, cross-origin images, fixed positioning and very tall dialogs.
What html2canvas actually captures
html2canvas walks the selected element, reads its computed styles and paints a representation into a canvas in the browser. It can capture a modal or any other DOM subtree without navigating away or asking the user for a system screenshot. Because it reconstructs the page, browser security rules and CSS support still apply: an image from another origin needs permission, and a CSS effect that the library does not implement may look different from the live dialog.
Install the package from npm:
npm install @html2canvas/html2canvas
In a browser module, import it and capture the dialog only after it is open, laid out and finished animating.
Basic modal-to-PNG implementation
This example assumes the dialog has id="my-modal". It keeps the background transparent, requests a high-density canvas, permits CORS-enabled images, and sizes the virtual window from the modal’s scroll dimensions.
#1 Best Overall
import html2canvas from '@html2canvas/html2canvas';
async function saveModal() {
const modal = document.querySelector('#my-modal');
if (!modal) throw new Error('Modal #my-modal was not found');
const canvas = await html2canvas(modal, {
backgroundColor: null,
scale: window.devicePixelRatio,
useCORS: true,
windowWidth: modal.scrollWidth,
windowHeight: modal.scrollHeight
});
canvas.toBlob((blob) => {
if (!blob) {
throw new Error('The browser could not create a PNG blob');
}
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'modal.png';
link.click();
URL.revokeObjectURL(url);
}, 'image/png');
}
document.querySelector('#save-modal')?.addEventListener('click', saveModal);
toBlob() is a good default for downloads and file uploads because it avoids building a large base64 string. If you need an inline image or a data URL, use canvas.toDataURL('image/png') instead:
const dataUrl = canvas.toDataURL('image/png');
document.querySelector('#preview').src = dataUrl;
Capture at the right moment
- Open the modal. Do not capture a hidden element with
display:none; it has no usable layout for the cloned render. - Wait for layout. If an opening animation changes size or position, wait for its final frame. A practical browser-side wait is
await new Promise(requestAnimationFrame), repeated if your framework needs more than one frame. - Wait for assets. Ensure images have loaded and any asynchronous text or data has been inserted before calling html2canvas.
- Select the root. Use an id, a class, or a framework ref pointing at the dialog container, rather than
document.body. - Export the result. Check that the callback receives a Blob before creating the object URL, then release that URL after starting the download.
A framework ref is equivalent to document.querySelector; the important detail is that the ref resolves to the visible modal root at capture time.
Preventing cropped fixed or scrollable dialogs
Modal backdrops are commonly fixed to the viewport while the dialog itself is centered, constrained by max-height, or internally scrollable. html2canvas renders a cloned document, so the viewport used for that clone matters. Set windowWidth and windowHeight to the dimensions needed by the modal’s content when the output is clipped. For a tall dialog, use its scrollWidth and scrollHeight as in the example.
const rect = modal.getBoundingClientRect();
const canvas = await html2canvas(modal, {
scrollX: window.scrollX,
scrollY: window.scrollY,
windowWidth: Math.max(modal.scrollWidth, Math.ceil(rect.width)),
windowHeight: Math.max(modal.scrollHeight, Math.ceil(rect.height)),
scale: Math.min(window.devicePixelRatio, 2)
});
Use the actual scroll offsets when the fixed element’s appearance depends on the page position. If the dialog intentionally shows only a scrollable viewport, capture that viewport element and its visible dimensions; if you want all content, capture the content container and size the virtual window for its full scroll dimensions.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
Do not assume that increasing dimensions indefinitely solves clipping. Browser canvas width and height limits vary by browser and device. A canvas that is too large can be blank or partial without throwing an obvious exception. Reduce scale, capture a smaller region, or split an exceptionally tall modal into several images.
Images, CORS and tainted canvases
Same-origin images normally work. An image hosted on another origin must be served with an appropriate Access-Control-Allow-Origin response header. Setting useCORS:true tells html2canvas to request CORS-enabled images; it cannot grant permission that the image server did not send.
- Preferred: host the image on the same origin as the page.
- Also valid: configure the image host to allow your page’s origin, then keep
useCORS:true. - When you control neither host: route the asset through a server-side CORS proxy for external images, or replace it with a same-origin copy.
If an external image is fetched without the required permission, the canvas may become tainted. Reading it with toBlob() or toDataURL() can then fail for security reasons. This is a browser boundary, not a setting html2canvas can bypass. Check image response headers in the browser’s network panel and make sure the URL is the one actually used by the modal, including responsive-image sources.
Sharper output without exhausting memory
The scale option controls the canvas pixel density. window.devicePixelRatio generally produces a crisp result on a Retina or other high-density display, while scale:1 uses CSS-pixel dimensions. Higher values increase memory and processing cost quadratically: doubling both width and height requires roughly four times as many pixels.
Rank #3
const dpr = window.devicePixelRatio || 1;
const scale = Math.min(dpr, 2); // a practical ceiling for large dialogs
const canvas = await html2canvas(modal, { scale });
Choose the smallest scale that meets the required print or UI quality. For a large report modal, a capped scale is safer than blindly using a very high device-pixel ratio.
Hide controls and transient UI
Close buttons, copy controls, loading spinners and hover-only overlays often do not belong in the exported image. Add data-html2canvas-ignore to an element that should be omitted:
<button type="button" data-html2canvas-ignore>Close</button>
<button type="button" data-html2canvas-ignore>Copy</button>
You can also omit elements programmatically with the configuration’s ignore predicate:
const canvas = await html2canvas(modal, {
ignoreElements: (element) =>
element.matches('.close-button, .temporary-overlay')
});
Keep the modal visible while capturing; ignoring an element is different from hiding the entire dialog.
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 errorsRank #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
Common failures and precise fixes
Blank or partially rendered canvas
- Inspect
canvas.widthandcanvas.height. Zero or unexpectedly huge values indicate a layout or canvas-limit problem. - Lower
scale, reduce the capture region, or split a very tall dialog. - Verify the modal was visible and had non-zero dimensions when the call began.
The modal is cropped
- Use
windowWidth: modal.scrollWidthandwindowHeight: modal.scrollHeightfor content that must fit. - Provide the relevant
scrollXandscrollYwhen fixed positioning depends on page scroll. - Capture the correct inner element: a scroll viewport produces only its viewport, while its content element can include the full scroll height.
Images are missing or export throws a security error
- Confirm the image is same-origin, or confirm its server sends
Access-Control-Allow-Origin. - Keep
useCORS:trueonly when the server is configured for CORS; otherwise use a proxy or same-origin asset. - Check every image, including CSS background images, because one disallowed resource can affect export.
CSS does not match the live modal
html2canvas supports many common CSS properties but is not a native browser compositor. Complex filters, transforms, unusual layout properties and other partially supported effects can differ. Simplify those effects for the capture variant, test the browsers you support, or use a native browser screenshot when pixel-identical rendering is required.
Unwanted buttons or overlays appear
Add data-html2canvas-ignore or an ignoreElements predicate to remove transient controls from the cloned render.
The capture races an animation or image load
Move the capture after the final animation frame and after the required image/data promises resolve. A screenshot call that runs immediately after opening the dialog can legitimately capture its intermediate state.
When html2canvas is the wrong boundary
Use html2canvas when the modal is in your page, you can select its DOM, and a reconstructed canvas is acceptable. It is especially convenient for client-side previews and downloads. Choose a native browser screenshot service when you need the browser’s own compositing, content outside your DOM, or server-side automation without exposing a capture button to visitors. The trade-off is straightforward: html2canvas is direct DOM integration; a browser screenshot is closer to what a user sees but requires browser automation or an API.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Best Value
Or skip the browser setup
ScreenshotNeo captures a URL through a website screenshot API, so you do not have to install a browser library or manage canvas limits. It accepts the consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture; each cleanup step can be disabled. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients.
For a publicly reachable page containing the modal, one GET request returns an image. See the ScreenshotNeo documentation for all options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Beyond a URL, ScreenshotNeo supports full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets plus custom viewports, Retina scale, PDF paper settings and page ranges, HTML/CSS-to-image, custom CSS and JavaScript, pre-capture clicks, hidden selectors, waits for selectors/delays/network idle, request and resource blocking, custom headers/cookies/user agents/Authorization, timezone and geolocation, transparent backgrounds, resizing, selectable-TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for 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 migration.
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is included on every plan, and yearly billing provides two months free. Create a free ScreenshotNeo account to start.
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 matchQuick decision checklist
- Need a client-side image of a DOM modal you control? Select the visible root and use html2canvas.
- Seeing clipping? Match virtual window dimensions to the modal’s scroll dimensions and provide fixed-position scroll offsets.
- Missing images? Fix same-origin/CORS delivery or use a proxy;
useCORSalone is not permission. - Need crisp output? Increase
scalecarefully and watch canvas limits. - Need a server-side, native browser capture? Use a screenshot API such as ScreenshotNeo instead of reproducing browser setup in the page.
Frequently Asked Questions
Can html2canvas capture a modal that is inside an iframe?
Only when the iframe content is accessible under the browser’s same-origin rules and you can select its document. Cross-origin iframe content is blocked by browser security and cannot be read by html2canvas.
Should I use toBlob or toDataURL for a modal download?
Use toBlob for a file or upload workflow; it avoids constructing a large base64 string. Use toDataURL when another API specifically requires an inline data URL.
Why does the PNG have a transparent background?
The example sets backgroundColor to null, which preserves transparency. Set a CSS color such as ‘#fff’ in the html2canvas options when the exported image needs an opaque background.
Can I capture only one element inside the modal?
Yes. Pass that child element instead of the modal root, or use ScreenshotNeo’s selector-based element capture when the page is being rendered through its API.
Recommended Free Tools
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.




