How to Take Chromium Screenshots with Puppeteer
Use Puppeteer’s Page.screenshot() to capture a Chromium viewport, full page, region, or element. Save PNGs and configure format, transparency, and readiness.
Puppeteer takes Chromium screenshots with page.screenshot(). Navigate to a page, call the method, and pass path to save the result. Use fullPage: true for the entire page, clip for a rectangle, or element.screenshot() for a particular DOM element.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' });
} finally {
await browser.close();
}
This saves a PNG in the process working directory. Puppeteer’s general screenshot API defaults to PNG. Page readiness is site-specific: navigation completing does not guarantee that every image, font, animation, or client-rendered component has reached the state you want.
1. Install Puppeteer and run a minimal capture
In a Node.js project, install Puppeteer and save the example as an ES module, such as screenshot.mjs:
npm install puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' });
console.log('Saved screenshot.png');
} finally {
await browser.close();
}
Run it with node screenshot.mjs. The finally block closes Chromium even if navigation or capture fails. Puppeteer’s screenshots guide demonstrates waiting for networkidle2 in an example, but no single navigation wait mode guarantees that all visual assets are ready on every site. Choose a readiness condition for the page you are capturing. See the Puppeteer Screenshots guide and Page API.
2. Choose what to capture
| Capture | How | Use it for |
|---|---|---|
| Viewport | page.screenshot() |
The currently visible browser area; this is the default. |
| Full page | page.screenshot({ fullPage: true }) |
A page-length image beyond the initial viewport. |
| Rectangle | page.screenshot({ clip: { x, y, width, height } }) |
A specific region in page coordinates. |
| DOM element | element.screenshot() |
A selected element, such as an article or chart. |
Capture the viewport
With no area option, Puppeteer captures the current viewport. Set its dimensions before navigation or capture when the screenshot needs a repeatable viewport:
await page.setViewport({ width: 1440, height: 900 });
await page.goto('https://example.com');
await page.screenshot({ path: 'viewport.png' });
Capture the full page
await page.goto('https://example.com');
await page.screenshot({ path: 'full-page.png', fullPage: true });
Full-page capture can produce a tall image. Lazy-loaded content may not appear unless it has been triggered or loaded before capture. If the page loads content while scrolling, scroll through it deliberately, wait for the relevant content, then capture. Full-page behavior and available options depend on the protocol in use; see the protocol notes below.
Capture a rectangle
await page.goto('https://example.com');
await page.screenshot({
path: 'region.png',
clip: { x: 100, y: 200, width: 640, height: 400 }
});
The clip describes a rectangle using x, y, width, and height. Check that its dimensions are positive and that its position corresponds to the intended page coordinates. The options reference documents captureBeyondViewport as defaulting to false when there is no clip and true when a clip is provided; set it explicitly if capture beyond the viewport matters. Consult the installed version’s reference before relying on protocol-specific behavior.
Capture a particular element
const element = await page.waitForSelector('main article');
if (!element) throw new Error('Article not found');
await element.screenshot({ path: 'article.png' });
waitForSelector() makes the selector wait explicit. ElementHandle.screenshot() scrolls the element into view if needed and throws if the element has been detached from the DOM. A selector that matches several elements may not identify the one you intend; make it specific and check the page’s structure. See ElementHandle.screenshot().
3. Wait for the page state you need
Choose readiness based on the page’s behavior. A static document may be ready after navigation. An application may render its main content later, and a page with lazy images may load them only after scrolling. You can wait for a specific selector when that element signals that the content you need is present:
await page.goto('https://example.com/report');
await page.waitForSelector('[data-report-ready="true"]');
await page.screenshot({ path: 'report.png', fullPage: true });
Replace the selector with one your target page actually exposes. If you use a fixed delay, it can waste time on fast responses and still be too short on slow ones. Network-idle waits can also be unsuitable for applications that keep requests open or poll continuously. Treat readiness as a property of the target page, not a guarantee from one universal wait setting.
4. Configure the image output
| Option | Effect | Important detail |
|---|---|---|
path |
Writes the screenshot to a file. | Optional; a relative path resolves from the process working directory. |
type |
Selects the image format. | Documented default is png; with a path, Puppeteer can infer the type from the extension. |
quality |
Sets image quality for supported formats. | Range is 0–100; it does not apply to PNG. |
omitBackground |
Hides the default white background for transparency. | Default is false. |
encoding |
Controls returned data encoding. | Default is binary Uint8Array; base64 returns a base64 string. |
optimizeForSpeed |
Enables the documented speed-oriented option. | Default is false; the reference does not quantify a performance effect. |
Save JPEG or WebP
Use a format that your installed Puppeteer version supports. For formats with quality settings, choose quality based on your image and storage or transfer needs; a quality value is not meaningful for PNG.
await page.screenshot({
path: 'page.jpg',
type: 'jpeg',
quality: 80
});
Return bytes or base64 instead of saving a file
const imageBytes = await page.screenshot();
// imageBytes is a Uint8Array by default.
const base64Image = await page.screenshot({ encoding: 'base64' });
// base64Image is a base64 string.
When you omit path, the screenshot data is returned instead of being written to a file. If you need a file, either pass a path or write the returned bytes using Node.js file APIs. See the ScreenshotOptions reference and Page.screenshot() reference.
Use a transparent background
await page.screenshot({
path: 'transparent.png',
omitBackground: true
});
This omits Chromium’s default white background so transparent page areas can remain transparent. The page itself may paint a background, which this option does not remove.
5. Save the screenshot to a specific location
An absolute path avoids ambiguity about the process working directory. Ensure the destination directory exists and that the process can write to it.
await page.screenshot({ path: '/tmp/example.png' });
For a relative path such as screenshots/page.png, create the screenshots directory first if it does not exist. The Puppeteer API documents that a relative path resolves against the current working directory. If saving fails, inspect the thrown error and check the directory path and permissions.
6. Use options compatible with the browser protocol
Puppeteer’s general API and its WebDriver BiDi support do not document identical screenshot option sets. The BiDi support page lists clip, encoding, and fullPage. Verify current support before using options such as omitBackground, quality, or captureBeyondViewport in BiDi mode. A working option in one protocol mode should not be assumed to work in another. The documentation reviewed for this guide displays Puppeteer version 25.12.0; check your installed version’s documentation for version-specific behavior. See Puppeteer WebDriver BiDi support.
7. Troubleshoot common screenshot problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Screenshot is blank or missing page content | Capture ran before the page rendered the content you need. | Wait for a page-specific selector or state before capturing. Navigation completion alone may not mean a client-rendered view is ready. |
| Images are missing in a full-page capture | Images are lazy-loaded or require scrolling into view. | Trigger the page’s loading behavior, wait for images, and then capture. |
waitForSelector() returns no element |
The selector does not match, the page is different than expected, or the element did not appear in time. | Confirm the selector against the rendered DOM and wait for the correct application state. |
| Element screenshot reports a detached node | The application replaced or removed the element after it was selected. | Wait for the final element state and acquire a fresh handle immediately before capture. |
| Output file cannot be written | The directory may not exist, the path may resolve somewhere unexpected, or the process lacks write access. | Use a known absolute path, create the directory, and check permissions. |
| Quality setting has no effect | quality is not applicable to PNG. |
Use a format that supports quality and confirm the selected type. |
| Transparent option is rejected or ignored | The selected protocol may not support omitBackground. |
Check the active protocol’s documented option subset; BiDi lists a narrower set. |
| Clip is misplaced or empty | Coordinates or dimensions do not describe the intended region. | Verify page coordinates and positive width and height; check whether capture beyond the viewport is needed. |
| Capture hangs or takes unexpectedly long | A readiness condition may never occur, or the page may keep network activity alive. | Use a page-specific wait with a suitable timeout and avoid assuming network idle works for every application. |
8. Performance, reliability, and cost considerations
Puppeteer runs Chromium as part of your capture workflow, so your application is responsible for launching and closing the browser, providing the runtime and resources, and handling failures. Reuse a browser process for multiple pages when your service design allows it, but isolate pages and clean them up according to your workload. The cited documentation does not provide throughput, latency, or resource benchmarks, so size infrastructure against your own pages and capture patterns.
Full-page captures and large viewports can produce more image data than a viewport capture. Pick the smallest capture scope and output format that meets your needs. Network and rendering time often depend on the target site; a short fixed delay does not make a capture reliable. Use explicit application readiness checks and handle navigation, selector, and file errors.
The browser setup also has an operational cost: compute, browser lifecycle management, and maintenance of the Puppeteer and Chromium versions you deploy. The Puppeteer documentation explains capture behavior, but does not supply a universal monetary cost or performance estimate. Measure the pages and runtime you actually use before estimating cost.
9. Or skip the browser setup
ScreenshotNeo provides a screenshot API and MCP server. One GET request returns an image or PDF; see the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners are accepted and removed before capture, along with supported newsletter popups and chat widgets; each step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers report the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for AI agents and MCP clients. - The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
10. Frequently asked questions
How do I take a screenshot with Puppeteer?
Navigate with page.goto(), then call page.screenshot(). Pass path to save it as a file.
How do I take a full-page screenshot?
Pass fullPage: true to page.screenshot(). For lazy-loaded page content, make it load before capture.
How do I screenshot a specific element?
Wait for and select the DOM node, then call its screenshot() method. The element method scrolls it into view and can fail if the node is detached.
How do I save the screenshot to a file?
Set path, for example { path: 'screenshot.png' }. Relative paths resolve from the process working directory.
Does Puppeteer return bytes or a base64 string?
By default, screenshot data is returned as a Uint8Array when no path is supplied. Set encoding: 'base64' to request a base64 string.


