Use a browser automation locator when you need the pixels the browser rendered: in Playwright, call locator.screenshot() on the element. Use html2canvas when the export must run inside your web page and a reconstructed image is acceptable. These methods solve different problems: Playwright captures rendered output, while html2canvas rebuilds an approximation from DOM and CSS.
Choose the right capture method
| Requirement | Best fit | Result and trade-off |
|---|---|---|
| Automated test, server job, or CI workflow | Playwright locator screenshot | Captures the browser-rendered element and saves a file or returns image bytes. |
| A button that lets visitors export content from your app | html2canvas | Runs in the page and creates a canvas/data URL, but reconstructs the image rather than reading browser pixels. |
| PNG, JPEG, or WebP data from a DOM node | modern-screenshot | Its documented domToPng(node) pattern returns image data; verify current CORS and embedding behavior before shipping. |
Decide using four questions: Do you need pixel fidelity? Must code run in the visitor’s tab? Does the element contain cross-origin images or frames? Do you need a file, data URL, or buffer?
Capture one rendered element with Playwright
Playwright launches a real browser, waits for a locator, and screenshots only its bounding box. This is usually the most faithful option for automation.
Install and create a script
npm init -y
npm install -D playwright
npx playwright install chromium
Create capture-element.mjs:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1440, height: 1000 },
deviceScaleFactor: 1
});
await page.goto('https://example.com', { waitUntil: 'networkidle' });
const card = page.locator('.header').first();
await card.waitFor({ state: 'visible' });
await card.screenshot({ path: 'element.png', type: 'png' });
await browser.close();
Replace .header with a stable selector such as an ID, data attribute, or semantic role. The screenshot is clipped to that element, including its visible padding, borders, backgrounds, and descendants.
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 →#1 Best Overall
Return bytes instead of writing a file
const imageBytes = await page.locator('#invoice').screenshot({
type: 'webp',
quality: 85
});
// Upload imageBytes to object storage or an HTTP response.
JPEG and WebP quality settings apply to lossy formats. PNG is lossless and has no quality parameter.
Make the capture deterministic
- Wait for a visible locator rather than relying only on a fixed delay.
- Use
waitUntil: 'networkidle'for pages that finish loading their data quickly; for streaming apps, wait for a specific content selector instead. - Set a known viewport and device scale factor so output dimensions do not vary by machine.
- Disable animations in a test stylesheet or wait until transitions finish.
- For lazy content, scroll the target into view before capture.
await page.locator('#chart').scrollIntoViewIfNeeded();
await page.locator('#chart').screenshot({ path: 'chart.png' });
Element versus full-page screenshots
A full-page screenshot captures the entire document; a locator screenshot clips to one element. Prefer the locator when the target is known: it avoids manual cropping and preserves the browser’s actual layout at the selected viewport.
Export an element in the browser with html2canvas
html2canvas traverses a DOM node and paints a canvas from the properties it understands. It does not take a literal browser screenshot, so unsupported CSS, unreadable resources, and complex effects can differ from what users see.
Minimal download example
<button id="download">Download card</button>
<div id="capture">Your content</div>
<script type="module">
import html2canvas from 'html2canvas';
document.querySelector('#download').addEventListener('click', async () => {
const element = document.querySelector('#capture');
const canvas = await html2canvas(element);
const link = document.createElement('a');
link.download = 'capture.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
</script>
Install it with npm install html2canvas, or load the project’s browser bundle according to your build system.
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 reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteControl resolution, crop, and excluded nodes
const canvas = await html2canvas(document.querySelector('#capture'), {
scale: window.devicePixelRatio,
useCORS: true,
backgroundColor: '#ffffff',
ignoreElements: element => element.dataset.html2canvasIgnore === 'true'
});
const png = canvas.toDataURL('image/png');
A larger scale produces sharper output but consumes more memory. Mark controls or transient UI with data-html2canvas-ignore="true" when they should not appear.
Why the result may not match the page
The project documentation describes the limitation directly: “The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.” CSS features outside the library’s supported set, browser-native controls, filters, pseudo-elements, and canvas content can therefore differ. For visual regression or legal evidence, use a real-browser screenshot instead.
Cross-origin images, canvases, and iframes
Images and CORS
html2canvas can read an image only when it is same-origin or the image server grants access with CORS (or you provide a proxy). Set useCORS: true, but remember that the remote server must send an appropriate Access-Control-Allow-Origin header. Otherwise the canvas can become tainted and toDataURL() will fail.
Existing tainted canvases
If the element contains a canvas that previously drew unreadable cross-origin pixels, exporting the parent can still fail. Redraw that canvas from same-origin or CORS-enabled assets before capture.
Recommended Free Tools
Cross-origin frames
Browser security prevents html2canvas from reading a cross-origin iframe document. Sandboxed frames without allow-same-origin have the same restriction. Capture the framed application from its own origin, or use Playwright and target the frame’s page context where your authorization permits it.
Using modern-screenshot
The modern-screenshot package exposes a compact DOM-to-image API. A typical PNG flow is:
Rank #3
import { domToPng } from 'modern-screenshot';
const node = document.querySelector('#capture');
const dataUrl = await domToPng(node);
const link = document.createElement('a');
link.download = 'capture.png';
link.href = dataUrl;
link.click();
Check the current package release for supported CSS, font embedding, and CORS behavior. Its package documentation warns that partial embedding can fail when resources are blocked by CORS.
Production checklist
- Selector: use a stable, unique locator and fail clearly when it is missing.
- Fonts: wait for web fonts before capture when typography matters.
- Images: wait for each image’s
completestate and verify natural dimensions. - Animations: pause or disable motion to prevent inconsistent frames.
- Privacy: remove tokens, personal data, and hidden fields before writing or uploading an image.
- Memory: large elements at high scale create large canvases; lower scale or capture smaller regions when exports approach browser limits.
- Retries: retry navigation and transient network failures, but do not blindly retry a deterministic selector or CORS error.
- Validation: check file type, dimensions, and non-zero byte length before publishing the result.
Troubleshooting
“Locator resolved to multiple elements”
Make the selector specific, use .first() only when the first match is intentional, or iterate over all matching nodes and give each output a distinct filename.
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 minuteThe element is empty or clipped
Wait for the element’s content selector, scroll it into view, and confirm that a parent does not have overflow: hidden or a zero height. In Playwright, inspect the locator’s bounding box before taking the screenshot.
Fonts or images are missing
Wait for the relevant network requests or DOM state. For html2canvas, ensure image URLs are same-origin or CORS-enabled; useCORS cannot override a server policy.
SecurityError: Tainted canvases may not be exported
Remove unreadable cross-origin images or serve them with CORS. An already tainted canvas must be redrawn from accessible sources.
Animations produce different captures
Inject a stylesheet that sets transition and animation duration to zero, or wait for a stable state before calling the screenshot method.
Playwright cannot launch in CI
Install the browser binaries in the build image with npx playwright install chromium. If the environment is containerized, use the runtime’s documented sandbox configuration rather than disabling security blindly.
Or skip the browser setup
ScreenshotNeo captures a specific CSS-selected element through an API, so you do not need to manage Playwright binaries or a rendering server. Its cleanup step accepts cookie banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
Use the ScreenshotNeo API documentation for the complete option list. The API supports element selectors, full-page and lazy-image capture, dark mode, device presets or custom viewports, retina scale, PDF output, custom CSS and JavaScript, pre-capture clicks, wait conditions, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Adapt the URL and add the element-capture parameter described in the documentation.
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 →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(`${res.status} ${await res.text()}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every feature is on every plan: Free provides 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. See the free ScreenshotNeo sign-up to begin.
ScreenshotNeo plans
| Plan | Price | Included shots |
|---|---|---|
| Free | $0 | 1,000/month, no card |
| Starter | $5 | 3,000 |
| Growth | $15 | 15,000 |
| Pro | $39 | 60,000 |
| Scale | $99 | 250,000 |
| Business | $249 | 1,000,000 |
Yearly billing gives two months free. The lowest paid plan is $5 for 3,000 shots.
Frequently Asked Questions
Can I screenshot a hidden element?
A browser screenshot captures what is laid out and rendered. Make the element visible and give it dimensions before capture; otherwise the result may be empty or have a zero-size bounding box.
Which format should I choose?
Use PNG for lossless text and UI, JPEG for smaller photographic files, and WebP when you want a modern format with configurable quality and broad current browser support.
Is html2canvas suitable for visual regression tests?
Usually not when pixel accuracy is required. It reconstructs DOM content and can differ from browser pixels; use Playwright’s real-browser screenshot for regression comparisons.
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.




