Puppeteer Screenshot of an Element by CSS Selector
Capture one DOM element with Puppeteer: select it, wait for it to appear, and save its screenshot. Includes output options and fixes for common failures.
To capture one element with Puppeteer, wait for its CSS selector, then call screenshot() on the returned element handle:
const element = await page.waitForSelector('.target');
if (!element) throw new Error('Target element was not found');
await element.screenshot({ path: 'element.png' });
Puppeteer scrolls the element into view if needed, then captures that element. This is different from page.screenshot(), which captures the page or a page region. See Puppeteer’s screenshot guide and the ElementHandle screenshot reference.
1. Complete runnable example
Install Puppeteer, save this as capture-element.js, and run it with Node.js. Puppeteer launches its bundled browser by default.
npm install puppeteer
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 900 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
const selector = 'h1';
const element = await page.waitForSelector(selector, { timeout: 10000 });
if (!element) {
throw new Error(`No element matched selector: ${selector}`);
}
await element.screenshot({ path: 'element.png' });
await element.dispose();
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
waitForSelector() waits for the element to be present in the DOM. The screenshot method may scroll it into view. Choose a selector that identifies the intended element uniquely; for repeated matches, Puppeteer returns the first match.
2. Choose and wait for the right element
CSS selector examples
| What to select | Example | Notes |
|---|---|---|
| ID | #hero |
Usually a good choice when the page has a stable, unique ID. |
| Class | .product-card |
May match multiple elements; confirm the first match is the one you want. |
| Attribute | [data-testid="summary"] |
Useful when the page exposes a stable testing attribute. |
| Descendant | main article h2 |
Scopes the match to a specific part of the document. |
For a hidden element, use the visible option so Puppeteer waits until it is visible. If the site renders the target after an interaction, perform that interaction first, then wait for the target.
const element = await page.waitForSelector('[data-testid="report"]', {
visible: true,
timeout: 15000
});
Puppeteer recommends locators for selecting and interacting with elements because they wait for the element to be in a suitable state. The screenshot guide demonstrates the lower-level ElementHandle approach for this capture task; use waitForSelector() when that is the API you need. See the page interactions guide.
3. Screenshot output options
ElementHandle.screenshot() supports screenshot options and returns image bytes by default. Set encoding: 'base64' when you specifically need a base64 string. Refer to Puppeteer’s ScreenshotOptions reference for the option set supported by your installed version.
// Save to a file. The extension can determine the format.
await element.screenshot({ path: 'card.webp', type: 'webp' });
// Receive a base64-encoded string instead of writing a file.
const base64 = await element.screenshot({ encoding: 'base64' });
| Option | Use | Practical note |
|---|---|---|
path |
Write the result to a file. | Without a path, use the returned bytes in memory. |
type |
Choose a supported output format such as PNG, JPEG, or WebP. | When saving, use a matching filename extension. |
quality |
Adjust lossy image quality where supported. | PNG ignores this setting. |
omitBackground |
Capture with a transparent background where supported. | Useful for compositing; it does not make opaque content transparent. |
clip |
Capture a rectangular region. | For an element-specific capture, the element screenshot method already targets the selected element. |
fullPage |
Capture the whole page. | This is a page-wide option, not a way to expand a selected element capture to the whole document. |
Options and accepted formats can vary by Puppeteer version and browser. Check the reference for the version installed in your project if an option is rejected.
4. Dynamic pages, scrolling, and DOM changes
- Wait for page content: navigation completion does not guarantee that a client-rendered widget has appeared. Wait for the actual selector, or wait for a page-specific state before taking the screenshot.
- Off-screen target: Puppeteer’s element screenshot method attempts to scroll the element into view automatically.
- Lazy-loaded images: scrolling the target into view may trigger nearby lazy content, but do not assume every page has finished loading its images. Wait for the images or page state your capture requires.
- Rerendering: a framework may replace the node after you select it. If the handle becomes detached, locate the element again after the update and capture the new handle.
- Overlays: cookie banners, sticky headers, or dialogs can cover content in the captured area. Close or handle them before capture if the page permits it.
// Reacquire after an action that can rerender the page.
await page.click('button.refresh');
await page.waitForSelector('[data-testid="report"]', { visible: true });
const report = await page.$('[data-testid="report"]');
if (!report) throw new Error('Report disappeared before capture');
await report.screenshot({ path: 'report.png' });
5. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Waiting for selector ... failed |
The selector is wrong, the page has not rendered the element, or the target is inside a frame. | Inspect the selector, wait for the page state that creates it, and use the frame containing the element when applicable. |
Node is detached from document |
The page replaced or removed the selected node after lookup. | Wait for the rerender to finish and reacquire the element immediately before capture. |
| The wrong matching element was captured | The selector matches several nodes and the first match is not the intended one. | Make the selector more specific or select the desired match explicitly. |
| The result is clipped or unexpectedly sized | The element’s rendered dimensions, transforms, or overflow rules differ from expectations. | Inspect the element’s layout and page viewport; capture after fonts and content settle. |
| Capture hangs or navigation times out | The site keeps network connections open or never reaches the chosen navigation condition. | Use an appropriate navigation wait condition, then wait for the target selector rather than requiring network silence in every case. |
| Browser launch fails | The runtime lacks browser dependencies or cannot access the browser executable. | Install the required runtime dependencies, verify the Puppeteer browser installation, and consult the official troubleshooting guide. |
6. Performance, reliability, and cost
Element capture is useful when you need a specific card, chart, or report region instead of a full page image. Browser startup and page loading are often the larger costs than the final element capture. For repeated captures, reuse a browser process where appropriate, isolate each page or task, and close pages and handles when finished. Limit concurrency to what the host can support; each active browser page consumes resources.
For reliable output, wait on the target and any content it depends on, use stable selectors, and reacquire handles after page updates. A network-idle condition can be unsuitable for pages with long-lived requests, so select the wait condition based on the site. Puppeteer itself has no per-screenshot service fee; operating cost comes from the machine, browser runtime, and any infrastructure you use.
Or skip the browser setup
If you need an element-only screenshot, Puppeteer gives you direct DOM control. If a full-page or URL-based capture is enough, ScreenshotNeo is a website screenshot API and MCP server: one GET request returns an image or PDF, without managing a browser for the capture. Its API also supports CSS-selector element capture. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Every feature is on every plan.
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 API documentation for request parameters, including element selection. Sign up for 1,000 free screenshots a month with no card.
Frequently asked questions
Can I take the screenshot without saving a file?
Yes. Without a path, the screenshot call returns image bytes. Use the base64 encoding option if your caller needs a base64 string.
Does an element screenshot include content outside the element?
No. It captures the selected element’s rendered area. Use a page screenshot if you need the surrounding page.
Can I use a locator to take the screenshot?
The documented screenshot flow uses an ElementHandle. Locators are recommended for many selection and interaction tasks, but use the documented handle flow for this element screenshot procedure.


