Puppeteer Screenshot Clips: Capture a Selected Area of a Web Page
Use Puppeteer’s clip option to save a precise rectangular area, or capture a DOM element by its rendered bounds. Includes output options and troubleshooting.
Use page.screenshot() with a clip object to capture a rectangular region by its viewport coordinates. The rectangle is defined by x, y, width, and height. If the target is a known DOM element, elementHandle.screenshot() is usually easier because Puppeteer uses the element’s rendered bounds.
1. Capture a rectangular region with clip
Install Puppeteer in a Node.js project, then run this complete example. It opens a page, waits for the document to load, and saves the specified rectangle as a PNG.
npm install puppeteer
// capture-clip.js
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' });
await page.screenshot({
path: 'clip.png',
clip: { x: 40, y: 80, width: 640, height: 360 },
});
} finally {
await browser.close();
}
})();
The coordinates and dimensions are in CSS pixels. Here the capture begins 40 pixels from the left and 80 pixels from the top, and covers a 640 by 360 pixel rectangle. Adjust them for the viewport and layout you are capturing.
Puppeteer’s screenshot guide documents Page.screenshot() for page captures and ElementHandle.screenshot() for element captures. See the Puppeteer screenshots guide and the ScreenshotClip API.
2. Choose coordinates or an element
| Approach | Use it when | Selection behavior |
|---|---|---|
clip |
You need a specific rectangle, perhaps spanning multiple elements or including surrounding space. | You provide x, y, width, and height. |
elementHandle.screenshot() |
You know the CSS selector for the target and want its rendered bounds. | Puppeteer captures the selected element; it attempts to scroll a hidden element into view first. |
fullPage: true |
You need the entire page rather than one selected area. | Captures the full page; it is a separate option from clipping. |
For an element, query it and call its screenshot method:
const card = await page.$('.product-card');
if (!card) throw new Error('Could not find .product-card');
await card.screenshot({ path: 'product-card.png' });
Use coordinates when the selection is not one element or when you need a fixed crop. Prefer an element handle when a selector describes the target more reliably than hard-coded coordinates.
3. Configure the screenshot output
The options below cover the common output and capture decisions. Consult Puppeteer’s ScreenshotOptions reference for the full current interface.
| Option | Behavior and notes |
|---|---|
path |
Writes the image to disk. The file extension is used to infer the format. If omitted, Puppeteer returns the image data without writing a file. |
clip |
Rectangle with x, y, width, and height. Its optional scale defaults to 1. |
type |
Image format: PNG by default, or JPEG or WebP. |
quality |
Applies to non-PNG images and ranges from 0 to 100. Higher values generally preserve more image detail while producing larger output. |
omitBackground |
When true, omits the default white background so transparent areas can remain transparent. |
captureBeyondViewport |
Defaults to true when a clip is present and false when there is no clip. Browser and version behavior can vary. |
fullPage |
Captures the full page. This is a different goal from capturing a selected rectangle. |
encoding |
Defaults to image bytes as a Uint8Array. Set encoding: 'base64' to receive a base64 string. |
For example, save a clipped region as WebP, or consume the returned bytes directly:
const imageBytes = await page.screenshot({
clip: { x: 40, y: 80, width: 640, height: 360 },
type: 'webp',
quality: 85,
});
// imageBytes is a Uint8Array by default.
require('node:fs').writeFileSync('clip.webp', imageBytes);
If you want base64 instead, pass encoding: 'base64' and handle the returned string accordingly. For transparent output, use PNG with omitBackground: true where transparency is needed.
4. Make captures repeatable
- Set the viewport. Use
page.setViewport()before navigation so layout and coordinates are predictable. - Wait for the content you need. A page load event may happen before client-rendered content or images are ready. Wait for a selector, a specific application condition, or an appropriate navigation state before capturing.
- Measure the rendered region. Inspect the page at the same viewport size. If using fixed coordinates, verify the rectangle against the layout being captured.
- Capture after layout settles. Fonts, image loading, animations, and responsive changes can shift the target. Where appropriate, wait for the target selector and disable animations in a test-specific stylesheet.
- Check the output. Open the resulting file and confirm the crop, format, dimensions, and content before using it downstream.
For a stable element target, read its bounds and use them to construct a clip if a rectangular clip is needed:
const target = await page.$('.report-panel');
if (!target) throw new Error('Could not find .report-panel');
const box = await target.boundingBox();
if (!box) throw new Error('Target has no visible bounding box');
await page.screenshot({
path: 'report-panel.png',
clip: { x: box.x, y: box.y, width: box.width, height: box.height },
});
A bounding box can be unavailable for an element that is not rendered. Check visibility and page state before relying on it.
5. Troubleshoot common problems
| Symptom | Likely cause | Fix |
|---|---|---|
| The clip is in the wrong place | The coordinates were measured at a different viewport size, or the page layout shifted. | Set the viewport before navigation and capture; recheck coordinates after fonts and dynamic content settle. |
| The output is blank or missing content | The page has not rendered the target yet, or the target is outside the rendered area. | Wait for the target selector or application state. For element capture, verify the selector found a rendered element. |
| An element screenshot fails to find the target | The selector is wrong, the element is created later, or it is inside a frame. | Wait for the selector, check the match, and use the appropriate frame context when the target is inside an iframe. |
| The image is not saved | No path was provided. |
Set path, or write the returned Uint8Array yourself. |
| The output format is unexpected | The extension and explicit type do not agree, or the extension was omitted. |
Use a matching filename extension and type; remember PNG is the default. |
| Transparent areas appear white | The default page background is included. | Use omitBackground: true and a format that supports transparency, such as PNG. |
| Capture hangs or takes too long | A network-idle condition may never occur on pages with continuing requests, or navigation is waiting on a slow resource. | Choose a wait condition suited to the page, wait for the specific content instead, and set an explicit timeout strategy for navigation and selectors. |
| Clip dimensions are invalid | A width or height is zero, negative, or otherwise not a usable rectangle. | Provide positive dimensions and check the values used to form the clip. |
6. Performance, reliability, and cost
Clipping limits the captured output region, but Puppeteer still has to launch or use a browser, load the page, and render enough content to take the screenshot. Reuse a browser process for multiple captures where appropriate, while isolating pages and cleaning them up reliably. A fixed clip is quick to specify but fragile when layouts change; selector-based element capture is often more robust for responsive pages.
Network-idle waits can be unreliable on sites with long-lived requests or analytics traffic. Waiting for a meaningful selector or page-specific readiness condition can reduce unnecessary delay. Add timeouts and close the browser in a finally block so failures do not leave browser processes running.
Self-hosted Puppeteer has no per-screenshot API fee from Puppeteer itself, but it uses compute, memory, and browser maintenance resources in your environment. Factor in those resources and operational work when capturing at scale. Output size depends on the selected area, image content, format, and quality setting.
7. Or skip the browser setup
If you only need a screenshot and do not want to run a browser, ScreenshotNeo provides a screenshot API and MCP server. Its element capture option accepts a CSS selector; for a full screenshot, make one GET request:
ScreenshotNeo API documentation
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo 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, failed loads, timeouts, and cache hits are never billed. Its MCP server lets AI agents take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots.
Sign up free for 1,000 screenshots a month, no card required.
8. Frequently asked questions
Does clip use viewport coordinates or document coordinates?
Think of the values as coordinates for the page capture at the layout and viewport you set. Confirm them at the same viewport and page state you will use for the screenshot.
Does Puppeteer save a screenshot when path is omitted?
No. Without path, the screenshot data is returned to your code; write those bytes yourself if you need a file.
Is fullPage the same as clip?
No. fullPage: true captures the whole page. Use clip to select one rectangle.
Which Puppeteer version do these options apply to?
The cited API reference displayed version 25.12.0 when researched on October 3, 2026. Check the current documentation for behavior in the version installed in your project.


