Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

With Puppeteer, wait for the element you want and call element.screenshot(). Puppeteer scrolls the element into view if needed and captures its rendered bounds—not the whole page. For an element screenshot, this is usually simpler and safer than calculating a crop rectangle yourself.

Capture one element with Puppeteer

Install Puppeteer in your project if it is not already available, then use a selector that identifies the element to capture. This complete example waits for the page, waits for a visible target, and saves a PNG:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'networkidle2' });

    const element = await page.waitForSelector('#target', { visible: true });
    if (!element) throw new Error('Target element was not found');

    await element.screenshot({ path: 'element.png' });
  } finally {
    await browser.close();
  }
})();

Replace https://example.com with the page to visit and #target with a selector for the element. The selector can be a class, ID, or another CSS selector supported by the page. The visible: true option makes the wait require a visible element; it does not guarantee that the element has useful dimensions or that all its visual assets have loaded.

Puppeteer’s ElementHandle screenshot reference describes the method as scrolling the element into view when needed and then using the page screenshot mechanism. The official screenshots guide demonstrates the same wait-then-capture pattern.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make the capture match the intended visual state

Waiting for navigation to finish is not the same as waiting for every pixel you care about. Fonts, images, lazy-loaded content, animations, or client-side updates may still change the target after navigation. Prefer a page-specific readiness condition—such as a known “chart rendered” selector or completed application state—over an arbitrary sleep.

Wait for fonts and images when they matter

If the target depends on web fonts or images, wait for them before capturing. Puppeteer’s guide demonstrates waiting for document.fonts.ready and decoding current images. For example, after waiting for the target, you can add:

await page.evaluate(async () => {
  await document.fonts.ready;
  await Promise.all(
    Array.from(document.images, image => {
      if (image.complete) return Promise.resolve();
      return new Promise(resolve => {
        image.addEventListener('load', resolve, { once: true });
        image.addEventListener('error', resolve, { once: true });
      });
    })
  );
});

This waits for the page’s current image elements to complete, including images that report an error, so a broken image does not hold the script forever. It does not force a page to load content that has not yet been requested; trigger lazy loading or the relevant application action first if needed. For a particular element, a narrower readiness check may be more reliable than waiting on every image in the document.

Keep the target attached and measurable

The target must still exist when its screenshot is taken. If a framework replaces the node between the wait and capture, Puppeteer may report a detached-node error. Query the selector again after the DOM update and capture the fresh handle. A hidden or zero-size element may not yield a useful image even if a selector finds it; check the page’s state and dimensions when the output is blank or unexpectedly small.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the right capture method

Method Use it when What it does
ElementHandle.screenshot() You have a selector for one element. Captures the element’s rendered bounds and scrolls it into view if needed.
page.screenshot({ clip }) You already know, or need to calculate and reuse, a rectangle. Captures the specified page region using its coordinates and dimensions.
Chrome DevTools Protocol Page.captureScreenshot Your client already uses CDP and needs protocol-level controls or base64 image data. Captures a page screenshot with a protocol clip and image-format controls.
page.screenshot({ fullPage: true }) You need the document rather than a single element. Captures the full page, so it is not the focused choice for one element.

For ordinary selector-based work, start with ElementHandle.screenshot(). Use a clip when you need a precise rectangle independent of a particular node, or use CDP directly when your integration already operates at that level.

Capture a manually defined rectangle

A clip is useful when the desired crop is defined by coordinates, or when you need to compute those coordinates and reuse them. This example reads the target’s bounding rectangle and passes it to page.screenshot():

const box = await page.$eval('#target', el => {
  const r = el.getBoundingClientRect();
  return { x: r.x, y: r.y, width: r.width, height: r.height };
});

await page.screenshot({ clip: box, path: 'element-clip.png' });

Puppeteer’s ScreenshotOptions reference defines clip as the region to capture. captureBeyondViewport controls whether capture may extend outside the viewport; the documented default is false without a clip and true with a clip. Do not combine a manual clip with fullPage: true when your goal is a single element: they describe different capture scopes.

Unlike the element-handle method, a manual rectangle does not automatically follow the element if layout changes between measuring and capturing. Measure only after the page reaches the intended state, and avoid intervening layout changes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Control format, dimensions, and appearance

Puppeteer’s screenshot options support PNG, JPEG, and WebP. PNG is the normal default and is appropriate when you want lossless output; JPEG and WebP can produce smaller lossy images. The quality option applies to lossy formats where supported. Other relevant options include:

  • path: write the image to a file.
  • encoding: choose the returned data representation rather than relying only on a file path.
  • omitBackground: omit the default page background for transparency where supported.
  • clip and captureBeyondViewport: control a page-region capture.
  • fullPage: capture the document, not just one element.
  • fromSurface: control the screenshot source behavior exposed by the API.

When exact output dimensions matter, explicitly set the page viewport and device scale before navigation or capture. CSS pixels, device scale, and browser/platform rendering all affect the resulting image dimensions. A screenshot of an element reflects its current rendered CSS layout; it does not preserve an abstract component independent of the browser’s rendering choices.

Use Chrome DevTools Protocol directly

At the lower level, Chrome DevTools Protocol exposes Page.captureScreenshot. Its clip is a Page.Viewport containing x, y, width, height, and scale. The method returns base64 image data and supports PNG, JPEG, and WebP, along with capture and encoding controls. See the official CDP Page.captureScreenshot documentation.

CDP is not usually necessary just to screenshot a selector: Puppeteer’s element handle already performs the bounds and page-capture work. Use the protocol method when your application already speaks CDP or needs its direct controls and data format.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Troubleshoot common failures

The selector wait times out

  • Check that the selector matches the live page and that the target is not inside an iframe. A selector queried on the main page does not automatically search a separate frame.
  • If the element appears only after interaction or client-side rendering, perform that action or wait for the application-specific ready state before querying.
  • If it exists but is hidden, remove visible: true only if capturing a hidden-state element is actually useful; hidden content may have no meaningful rendered bounds.

The screenshot is blank, clipped, or the wrong size

  • Confirm the target has non-zero width and height and is in the intended visual state.
  • Wait for fonts, images, lazy-loaded content, and late DOM updates that affect the capture.
  • Set the viewport and device scale explicitly if output pixel dimensions matter.
  • If using clip, verify the measured coordinates and dimensions, and check whether the capture should extend beyond the viewport.

Puppeteer reports a detached node

The page removed or replaced the node after Puppeteer obtained its handle. Wait for the updated page state, query the selector again, and call screenshot() on the newly returned handle.

The capture changes between runs

Dynamic content, animation, delayed assets, and responsive layout can change the result. Stabilize the relevant page state, wait for the specific content that matters, and keep viewport and device scale consistent. A fixed delay can help diagnose timing, but a condition tied to the page’s actual readiness is generally more dependable.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you do not need to maintain a Puppeteer or Chrome capture workflow, ScreenshotNeo takes a screenshot through one GET request. Its clean-shot options accept cookie or consent banners like a visitor and remove more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides screenshot tools for Claude, Cursor, and other MCP clients.

For a normal page screenshot, the cURL call is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for authentication and request options. This endpoint captures a page from a URL; it is not a Puppeteer selector-handle call. For workflows that need a particular element, use the browser method above or adapt the capture around the page’s layout and ScreenshotNeo options.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

ScreenshotNeo offers 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000. Sign up for ScreenshotNeo’s free plan.

Performance, reliability, and cost considerations

A browser-based element capture requires the browser to load and render the page, then produce the image. For a single capture or a workflow requiring page interaction, Puppeteer gives you control over readiness, viewport, and the target node. Reusing a browser process across multiple page captures can avoid repeatedly launching a browser, but ensure each page is in the right state and close browser resources when finished.

Reliability depends more on page readiness and stable geometry than on choosing a screenshot API method. Wait for the actual visual dependencies, reacquire replaced nodes, and use a selector capture unless you have a reason to manage coordinates yourself. With ScreenshotNeo, only clean shots are billed; its response includes X-Page-Verdict and X-Billed headers so you can distinguish verdict and billing status. Its published tiers are Free: 1,000 shots/month; Starter: $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, and every feature is on every plan.

Frequently Asked Questions

Can Puppeteer screenshot an element that is below the fold?

Yes. `ElementHandle.screenshot()` scrolls the element into view if needed before capture.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Does an element screenshot include the whole page?

No. It captures the element’s rendered bounds. Use `page.screenshot({ fullPage: true })` when you need the document.

Which format should I use for an element screenshot?

PNG is the default and lossless; JPEG or WebP can reduce file size with lossy encoding.

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.