What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use Puppeteer’s ElementHandle.screenshot() method: select the DOM element, wait until it exists, then call screenshot() on its handle. Puppeteer scrolls the element into view as needed. If a page update detaches the element before capture, query it again and retry.
Capture an element and save it to a file
This example uses ECMAScript modules and writes a PNG to element.png. Install Puppeteer in your project first; the code assumes the package is available to Node.js.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
const element = await page.waitForSelector('.target-element');
if (!element) {
throw new Error('Target element was not found');
}
try {
await element.screenshot({ path: 'element.png' });
} finally {
await element.dispose();
}
} finally {
await browser.close();
}
Replace https://example.com with the page to capture and .target-element with a selector that identifies the desired element. The outer finally closes Chromium even if navigation or capture fails; the inner one disposes the handle after use.
How the element screenshot works
ElementHandle.screenshot(options?) captures the selected element, rather than the whole page. Puppeteer’s API documentation says the method scrolls the element into view if needed, then uses the page screenshot mechanism to capture it: ElementHandle.screenshot().
#1 Best Overall
By default, the method returns a Promise<Uint8Array>. Giving it a path writes the screenshot to that file. If a path is supplied, Puppeteer can infer the image type from the extension, so element.png, element.jpg, or element.webp requests the corresponding format where supported by the installed version and browser.
The element method accepts the screenshot options used by the page screenshot API. Options documented for that API include clipping, full-page capture, transparency, encoding, and quality. Quality applies to lossy formats, not PNG; PNG is the default output when no other type is selected. Check the documentation for the Puppeteer version installed in your project before relying on options that may differ by release: ScreenshotOptions.
Choose how to find and wait for the element
The selector method determines when your script can safely obtain a handle. For straightforward one-off captures, waitForSelector() is a direct fit. Puppeteer’s current interactions guidance recommends locators for typical selection and interaction because they wait automatically and check that elements are ready for the action.
Rank #2
| Approach | What it returns | Waiting behavior | Best fit |
|---|---|---|---|
page.waitForSelector(selector) |
An element handle when the selector appears | Waits for a match, subject to its wait options | A direct handle workflow for ElementHandle.screenshot() |
page.locator(selector).waitHandle() |
An element handle obtained from a locator | Uses locator waiting and readiness behavior | Scripts already using locators, or workflows where automatic readiness checks help |
page.$(selector) |
The first matching handle, or null |
Does not wait for a later match | Only when the element is expected to exist already and the null case is handled |
Wait for a selector
waitForSelector() resolves once the element appears. The Puppeteer screenshot guide demonstrates this handle-first pattern. Its result can be null in cases such as a selector configured to wait for hidden or absent content; retaining the explicit check prevents calling screenshot() on a missing handle. See Puppeteer screenshots.
Recommended Free Tools
Use a locator when readiness matters
A locator can produce the handle required by the screenshot method:
const element = await page.locator('.target-element').waitHandle();
try {
await element.screenshot({ path: 'element.png' });
} finally {
await element.dispose();
}
Locators support CSS selectors by default, along with documented text, accessibility, XPath, and shadow-root selector syntax. See Page interactions and Locator. A locator’s automatic checks do not eliminate every race: a site can still replace the node after the handle is obtained.
Use a non-waiting lookup only when appropriate
page.$('.target-element') returns the first match immediately or null. Check the result before calling methods on it:
const element = await page.$('.target-element');
if (!element) {
throw new Error('No element matched .target-element');
}
try {
await element.screenshot({ path: 'element.png' });
} finally {
await element.dispose();
}
This avoids an unhelpful null-property error, but it does not wait for client-side rendering. Puppeteer documents the nullable result for Page.
Make the capture representative of the page
Finding the node is only part of a useful screenshot. A page may render the element before its fonts, images, or data have settled. Choose a wait condition that matches the page rather than adding an arbitrary long delay to every run.
Rank #4
- Wait for the target: use
waitForSelector()or a locator when the page creates the element asynchronously. - Wait for a meaningful state: if the element is present before its content is ready, wait for a page-specific condition or visible text before capturing.
- Account for lazy content: an image inside the target may load only after scrolling. The element screenshot scrolls its target into view, but it cannot guarantee that every asynchronous asset has finished rendering; wait for the content your use case requires.
- Use a stable target: prefer a selector tied to a stable id, data attribute, or component boundary over a fragile positional selector.
These are application-level timing choices: Puppeteer’s element screenshot API handles scrolling and capture, while your script must determine when the page’s content is ready for the intended result.
Handle common errors
| Symptom | Likely cause | Practical fix |
|---|---|---|
| Cannot read properties of null, or no method exists on the result | page.$() found no match, or a wait returned no visible handle |
Check for a missing handle before capture. Confirm the selector against the rendered DOM, and use waitForSelector() or a locator if the element appears later. |
| Element is detached from the DOM | A rerender or navigation replaced the selected node between selection and capture | Wait until the update finishes, query the element again, and capture the newly connected handle. Do not keep reusing the stale handle. |
| Screenshot includes an unexpected scroll position | The target was outside the viewport | This is normally handled automatically: the screenshot method scrolls the element into view. If the page has scroll-triggered content, allow it to settle after that movement before capturing. |
| Capture succeeds but the image is incomplete | The page or an asset inside the target had not finished rendering | Wait for a page-specific readiness signal, such as the expected text or image state, then take the screenshot. |
| Wrong element is captured | The selector matches a different or first repeated instance | Make the selector more specific and verify which node it resolves to before capture. |
| Process hangs or leaves browser processes behind | Browser cleanup is skipped after an exception | Put browser.close() in a finally block, as in the complete example. |
Use the returned image bytes instead of a file
When you need to upload the image, store it, or send it elsewhere, omit path and use the returned bytes. With Puppeteer’s documented default overload, the result is a Uint8Array:
const element = await page.waitForSelector('.target-element');
if (!element) throw new Error('Target element was not found');
try {
const imageBytes = await element.screenshot();
// Pass imageBytes to your storage or upload code.
} finally {
await element.dispose();
}
To request a base64 string, set encoding: 'base64'; the API exposes a corresponding overload. Choose bytes for binary file or upload handling, and base64 only when the receiving interface expects a text representation.
Best Value
- Used Book in Good Condition
Or skip the browser setup
If you need a hosted screenshot instead of controlling a local Puppeteer browser, ScreenshotNeo accepts a URL through one GET request and returns an image or PDF. This API captures a page from a URL; it is not a drop-in replacement for Puppeteer’s ability to select an arbitrary element in your own browser session. Its documented capture options include selecting an element by CSS selector.
For example, use the selector option shown in the ScreenshotNeo API documentation to request an element capture:
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://stripe.com
-d selector=".target-element"
-o element.webp
In ScreenshotNeo’s documented product description, cookie/consent banners, newsletter popups, and chat widgets are removed before capture, with those cleanup steps individually switchable. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed; the response identifies page verdict and billing status in headers. An MCP server offers take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000.
Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month without a card.
Free tools Windows power users keep installed
One-click scans. No signup required.
Version and compatibility notes
The Puppeteer API reference consulted for this article reports version 25.12.0; the screenshots guide and ElementHandle class documentation are labeled Next. If your project uses an older Puppeteer release, check the documentation corresponding to that installed version before depending on an option or overload. The core pattern remains to obtain a live element handle and call its screenshot method, but method signatures and supported options should be verified against the version you run.
Frequently Asked Questions
Can Puppeteer capture an element that is outside the viewport?
Yes. ElementHandle.screenshot() scrolls the target into view when needed before capturing it.
Does an element screenshot return bytes or a base64 string?
It returns a Promise of Uint8Array by default. Set encoding to ‘base64’ to request a base64-string result.
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.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problems

