ScreenshotNeo

BlogHow-to

How to Fix Cut-Off Full-Page Screenshots in Puppeteer

If a Puppeteer screenshot misses the bottom of a page, check fullPage, clip, page readiness, and the browser version before changing capture settings.

By the ScreenshotNeo team4 October 20266 min read

If a Puppeteer screenshot stops at the viewport or misses the bottom of a page, first set fullPage: true. Then check whether a clip bounds the capture, whether the page’s content has actually rendered, and whether production uses a different Puppeteer or Chrome executable than your local environment.

1. Set fullPage explicitly

Puppeteer’s fullPage option defaults to false. Set it to true when you want the full document:

await page.screenshot({
  path: 'page.png',
  fullPage: true,
});

See the Puppeteer ScreenshotOptions API. If you already set fullPage: true, continue with the checks below instead of assuming another option is a universal fix.

2. Check clip and captureBeyondViewport

clip describes a specific page region to capture. Its coordinates and dimensions can intentionally limit the result, so look for it in the options passed to page.screenshot() and remove or correct it if you need the full document. captureBeyondViewport is a separate setting: its documented default is false when no clip is supplied and true otherwise. These options have distinct behavior; setting captureBeyondViewport: true is not a general replacement for fullPage: true.

// Full document: do not pass a clip that restricts the capture.
await page.screenshot({ path: 'full.png', fullPage: true });

// A deliberate region: use clip only when that is the intended output.
await page.screenshot({
  path: 'region.png',
  clip: { x: 0, y: 0, width: 1200, height: 900 },
});

Log the final options object immediately before the screenshot call. This catches wrappers, defaults, or conditional code that may add a clip or override fullPage.

3. Wait for the content you need

A successful navigation does not prove that every page-specific asynchronous task has finished. Puppeteer’s screenshot guide demonstrates navigation with waitUntil: 'networkidle2', but client-side rendering, delayed content, and lazy loading may still require a page-specific readiness check. Wait for a meaningful selector or application state, then verify the target content exists before capture.

const response = await page.goto('https://example.com', {
  waitUntil: 'networkidle2',
  timeout: 30000,
});

if (!response || !response.ok()) {
  throw new Error(`Navigation did not return an OK response: ${response?.status()}`);
}

// Replace this with a selector that indicates the content you need is ready.
await page.waitForSelector('[data-report-ready="true"]', { timeout: 15000 });

const present = await page.$('.report-content');
if (!present) throw new Error('Expected report content is missing');

await page.screenshot({ path: 'report.png', fullPage: true });

Use a selector that reflects the actual content, rather than relying on a fixed delay as proof of readiness. If the site loads sections only after scrolling, reproduce the site’s loading behavior and verify those sections appear before capture. There is no universal scroll-and-wait recipe that works for every site.

4. Confirm the capture goal

  • Whole document: use page.screenshot({ fullPage: true }).
  • One component: use an element handle’s screenshot() method. Puppeteer attempts to scroll a hidden element into view by default.
  • A defined rectangle: use clip and check its x, y, width, and height.

These are different capture targets. Choose the one that matches the result you want rather than combining settings at random. See the Puppeteer screenshots guide.

5. Reproduce with the same browser runtime

Record the Puppeteer version and the actual Chrome or Chromium executable used where the screenshot fails. Puppeteer downloads a specific Chrome version by default, but can also be configured to use another executable. A custom binary is therefore a useful environment difference to inspect when local and production output differ; a version mismatch alone does not prove the cause.

console.log('Puppeteer version:', require('puppeteer/package.json').version);
console.log('Browser version:', await browser.version());
console.log('Browser executable:', browser.process()?.spawnfile);

Compare those values between environments, along with launch configuration and the final screenshot options. See Puppeteer configuration.

6. Diagnose exceptionally tall pages carefully

A Puppeteer issue opened in 2017 contains a user report of screenshots stopping at 16,384 pixels and, in some cases, producing white images. That is a historical report, not evidence of a current universal Chrome or Puppeteer limit. If the output is unusually tall, reproduce it using the exact production runtime, inspect the image’s actual dimensions, and compare the result with a smaller page or a targeted element capture.

Runnable Puppeteer example

This CommonJS example launches Puppeteer’s bundled browser, waits for navigation and an application-specific ready selector, then writes a full-page PNG. Install Puppeteer with npm install puppeteer, replace the URL and selector, and run it with Node.js.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 1365, height: 900 });

    const response = await page.goto('https://example.com', {
      waitUntil: 'networkidle2',
      timeout: 30000,
    });
    if (!response || !response.ok()) {
      throw new Error(`Navigation failed: ${response?.status()}`);
    }

    // Use a selector that appears only when the page's target content is ready.
    await page.waitForSelector('main', { timeout: 15000 });

    console.log({
      puppeteer: require('puppeteer/package.json').version,
      browser: await browser.version(),
      executable: browser.process()?.spawnfile,
    });

    await page.screenshot({ path: 'page.png', fullPage: true });
  } finally {
    await browser.close();
  }
})().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Common problems and fixes

Symptom Likely cause What to check
Screenshot is only the viewport fullPage omitted, false, or overwritten Set fullPage: true explicitly and log the final options.
Image ends at a particular coordinate A clip bounds the output Inspect clip coordinates and dimensions; remove it for a whole-page capture.
Bottom sections are blank or absent Content was not rendered or lazy-loaded before capture Wait for a page-specific ready selector and confirm the target elements exist. Trigger scrolling only if that page requires it.
Works locally, fails in production Different Puppeteer version, browser binary, or launch configuration Log and compare the actual versions, executable path, and screenshot options in both environments.
Very tall image is truncated or white Could be a runtime-specific issue, but the symptom alone does not establish a universal height limit Record output dimensions and reproduce with the same browser and Puppeteer versions; test a smaller or element-level capture.
Element screenshot omits a hidden target The element is not ready or is not the intended capture scope Wait for the element and use its handle’s screenshot(); Puppeteer attempts to scroll it into view.

Performance, reliability, and cost

Full-page capture can produce a much larger image than a viewport capture, especially on long documents. If you only need one component or a defined area, capture that target instead. For reliable automation, make readiness checks specific to the page, set navigation and selector timeouts, log the browser runtime and final options, and preserve the failing URL and output dimensions for reproduction. This diagnosis does not depend on any one historical pixel limit.

Running Puppeteer means maintaining a browser runtime and its launch configuration in the environment where the job executes. The sources cited here do not establish a general cost or speed figure; measure resource use with your own pages and workload.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API: one GET request returns an image or PDF. Cookie banners are accepted and removed before the shot, along with known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response includes page-verdict and billing headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture.

For a full-page capture, request full_page=true. See the ScreenshotNeo API documentation for the API options.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -d full_page=true \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": "YOUR_API_KEY",
        "url": "https://example.com",
        "full_page": "true",
    },
    timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
    f.write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com',
  full_page: 'true',
});
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 includes 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000. Create a free account and get 1,000 screenshots a month with no card.

FAQ

Does fullPage change the viewport size?

It requests a full-document screenshot. Set the viewport separately when the page layout depends on viewport dimensions.

Should I always enable captureBeyondViewport?

No. Its behavior and default depend on whether a clip is supplied. Diagnose the capture target and clip first.

Is 16,384 pixels Puppeteer’s current limit?

The cited 16,384-pixel observation comes from a 2017 user issue report; it does not establish a current general limit.