ScreenshotNeo

BlogHow-to

How to Take a Full-Page Screenshot in Chromium with Puppeteer

Use Puppeteer’s `fullPage: true` option to capture a Chromium page beyond the viewport. This guide covers setup, readiness, formats, errors, and alternatives.

By the ScreenshotNeo team4 October 20267 min read

To capture an entire page in Chromium with Puppeteer, call page.screenshot({ fullPage: true }). Add path to save the image. This runnable Node.js example opens Chromium, waits for navigation, saves a PNG, and closes the browser even if capture fails:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'full-page.png', fullPage: true });
} finally {
  await browser.close();
}

fullPage defaults to false. Puppeteer documents it as capturing the full page when set to true. See the Puppeteer screenshot guide and ScreenshotOptions reference.

1. Install Puppeteer and run the capture

In a new project, install Puppeteer:

npm init -y
npm install puppeteer

Puppeteer’s package downloads a compatible browser during installation. If your environment manages Chromium separately, puppeteer-core can connect to an explicitly configured browser, but the example below uses the standard puppeteer package.

Save the first example as capture.mjs and run:

node capture.mjs

The relative output path is resolved from the process’s current working directory. Provide an absolute path when a service, container, or scheduled job needs a predictable destination. If you omit path, the screenshot is returned as a Uint8Array and is not written to disk automatically.

2. Wait for the page content you need

The goto call’s waitUntil setting determines when navigation is considered complete. Puppeteer’s guide uses networkidle2 in its example. Treat that as a starting point: it does not guarantee that every application has finished rendering, that lazy images have loaded, or that content triggered by scrolling is present.

For an application with a known ready marker, wait for that marker explicitly:

await page.goto('https://example.com/report', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-report-ready="true"]');
await page.screenshot({ path: 'report.png', fullPage: true });

Replace the selector with one that your page sets only when the content to capture is ready. For a fixed, known delay, use await new Promise(resolve => setTimeout(resolve, 1000)) after navigation; a page-specific condition is usually more reliable than guessing a delay.

Lazy-loaded content

Some pages load images or sections only as they approach the viewport. A full-page screenshot captures the page’s current rendered state; it should not be treated as a guarantee that every lazy resource was requested first. When needed, scroll through the document before the final capture, then wait for the page’s content-ready condition:

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.evaluate(async () => {
  const step = Math.max(300, window.innerHeight);
  for (let y = 0; y < document.documentElement.scrollHeight; y += step) {
    window.scrollTo(0, y);
    await new Promise(resolve => setTimeout(resolve, 100));
  }
  window.scrollTo(0, 0);
});
await page.waitForSelector('footer');
await page.screenshot({ path: 'full-page.png', fullPage: true });

This is an example strategy, not a universal lazy-loading solution: page scripts can load content on different triggers, and a fixed pause may need adjustment. Prefer waiting for a concrete selector or application state when one is available.

3. Choose screenshot output and options

Page.screenshot() returns a Uint8Array by default. Set path to write a file. The image type can be set with type, or inferred from the file extension when saving. PNG is the default format.

Need Option Notes
Whole document fullPage: true Defaults to false.
Save to a file path: 'page.png' Relative paths use the current working directory. Without a path, Puppeteer does not save the image to disk.
Choose an image format type: 'png', 'jpeg', or 'webp' PNG is the default. File extensions can determine the format when using path.
Set lossy image quality quality: 80 Applies to non-PNG formats; it does not apply to PNG.
Capture a rectangle clip: { x, y, width, height } Use when you need a defined region instead of the whole page. See the API reference for clip behavior.
Transparent background omitBackground: true Hides the default white background where transparency is supported.
Return base64 encoding: 'base64' The result is a string rather than the default byte array.
Capture beyond the viewport captureBeyondViewport The documented default is false without a clip and true with a clip.

Example saving WebP with a quality setting:

await page.screenshot({
  path: 'full-page.webp',
  type: 'webp',
  quality: 80,
  fullPage: true,
});

Use fullPage for the complete document and clip for a specific rectangle. They express different capture goals; check the current ScreenshotOptions documentation when combining options or depending on viewport behavior.

4. Capture an element instead of the whole page

If the target is a single DOM element, get its handle and call ElementHandle.screenshot(). Puppeteer scrolls the element into view if needed. It throws if the element has detached from the DOM before capture.

const card = await page.waitForSelector('.report-card');
if (!card) throw new Error('Report card was not found');
await card.screenshot({ path: 'report-card.png' });

See ElementHandle.screenshot(). Use a selector tied to the intended content rather than a broad selector such as div, which may match the wrong element.

5. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns an image or PDF, and the parameter names used by other screenshot APIs also work. The ScreenshotNeo documentation covers its API options.

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}`);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed, and response headers identify the page verdict and billing status. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

6. Troubleshoot common capture problems

Symptom Likely cause What to do
The image shows only the viewport fullPage was omitted or set to false. Set fullPage: true in the screenshot call.
The screenshot file is missing No path was supplied, or a relative path resolved from a different working directory. Set path explicitly and use an absolute path when the process directory is uncertain. Without path, handle the returned bytes yourself.
The screenshot is blank or content is missing Capture ran before client-side rendering or page-specific content loading completed. Wait for a selector or application state that signals readiness. For lazy content, trigger the page’s loading behavior before capture.
A selector wait times out The selector is wrong, content never appeared, or the page is in a different state. Confirm the selector against the rendered page and check navigation or application errors before extending the timeout.
Element screenshot reports a detached node The framework replaced or removed the element after it was selected. Wait for the final UI state, then query the selector again immediately before calling screenshot().
JPEG or WebP quality has no effect The output is PNG, where the quality option does not apply. Choose a non-PNG format such as jpeg or webp before setting quality.
Capture fails on an exceptionally long page A very tall page can demand substantial browser and image memory; the reviewed documentation does not specify a universal maximum dimension or guaranteed success limit. Capture only the needed element or regions, reduce output dimensions where suitable, or split the task into smaller captures. Avoid assuming one fixed page-height limit.

7. Performance, reliability, and cost

Full-page captures can take longer and require more memory than viewport captures because more rendered content must be included. Large images also take more time and storage to write. Capture only what the task requires, choose a lossy format and quality when acceptable, and avoid launching more simultaneous browser work than the machine can handle.

For repeatable results, wait for the page condition relevant to the content, set the viewport deliberately when layout matters, use explicit output paths, and close the browser in a finally block. A successful navigation condition alone does not prove that a dynamic page is visually complete. Puppeteer is a library you run in your own environment, so browser execution and infrastructure costs depend on that environment; no universal cost or performance figure applies.

For managed capture without browser installation and lifecycle code, ScreenshotNeo offers 1,000 shots per month free with no card. Paid plans are Starter at $5 for 3,000, Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000; yearly billing gives two months free. Every feature is on every plan. See ScreenshotNeo for the product and its API documentation for capture configuration.

Frequently asked questions

Does fullPage: true change the viewport size?

It requests a full-page capture. The option is distinct from configuring the page viewport; use viewport settings when the responsive layout itself must match a particular screen size.

Can Puppeteer return screenshot data without creating a file?

Yes. Omit path and use the returned Uint8Array, or set encoding: 'base64' to receive a base64 string.

Should I use networkidle2 for every site?

No single navigation condition guarantees that every site has completed all visual work. Use a page-specific readiness signal when content appears after navigation or in response to scrolling.