Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
browser automation

Why Puppeteer Full-Page Screenshots Fail and How to Fix Them

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

Short answer: page.screenshot({ fullPage: true }) captures the document that exists at capture time; it does not load infinite-scroll content, guarantee a stable viewport, or wait for fonts, images, charts and application rendering. Make the viewport and page state deterministic, verify geometry and assets, then capture at deviceScaleFactor: 1 before adding complexity.

What fullPage actually does

Puppeteer’s fullPage option means “take a screenshot of the full page.” It expands the capture beyond the normal viewport to include the document’s current content. It is not an infinite-scroll loader and it is not a promise that every visual element has finished rendering.

The distinction explains many apparently random results:

  • If a page loads another batch only after a scroll event, that batch does not exist for Puppeteer unless your code scrolls far enough to trigger it.
  • If a chart, image, font or client-rendered component is still pending, the screenshot can contain a blank region even when navigation has completed.
  • If the capture changes the effective geometry, CSS using vw, vh, sticky positioning or fixed overlays can lay out differently from the normal browser view.

captureBeyondViewport is a separate control. Without a clip its default is false; with a clip its default is true. It can help when a regular viewport screenshot is stable but a full-page capture flashes or resizes, but it does not replace a correct readiness check.

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

Why full-page output is blank, clipped or the wrong width

Document geometry changes during capture

Full-page capture may temporarily use the document’s content dimensions. A reported Puppeteer failure mode is that the width grew to the content width, changing the meaning of vw and sometimes vh. A layout that looked correct at 1,440 CSS pixels can therefore reflow in the exported image. Treat this as a release- and Chromium-dependent failure mode, not as behavior guaranteed in every version.

Measure the page before capturing. Compare document.documentElement.scrollWidth, scrollHeight, the body dimensions and the target element’s bounding box. Unexpected horizontal overflow or a zero-sized target usually identifies the problem faster than inspecting the PNG.

Viewport flashing and capture-mode interactions

Older Puppeteer issue reports describe intermittent resizing and flashing during full-page capture. In one such case, setting captureBeyondViewport: false stopped the instability. Try that option when a normal viewport screenshot is reliable, then test an explicit clip or an element screenshot if the page still changes during capture.

deviceScaleFactor exposes a second class of bugs

Reports of white or distorted images at scale factor 2, especially when combined with fullPage, make high-density output a poor first diagnostic target. Start with deviceScaleFactor: 1. Once layout and capture are repeatable, test 2 with the exact Puppeteer/Chromium pair used in production and with the page’s maximum dimensions. A failure at 2 does not prove the page is broken at 1.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Viewport-relative, sticky and fixed CSS is capture-sensitive

100vh sections, vw-based grids, sticky headers and fixed consent bars are designed around a visible viewport. In a tall document capture they can appear repeated, cropped or repositioned. The safest export strategy is application-specific: add a temporary class that disables sticky and fixed behavior, or replace viewport-relative sizes with explicit export dimensions. Remove the class after the screenshot so normal visitors are unaffected.

Navigation completion is not visual readiness

waitUntil: 'networkidle0' only describes network activity. It does not prove that a chart has drawn, a font has been applied, a framework has committed its final state or a lazy image has entered the document. Prefer a deterministic application condition such as a “report-ready” marker, then wait for fonts, required images and positive element geometry.

A deterministic diagnostic sequence

  1. Set the viewport before navigation. Record width, height and device scale. Use CSS-pixel dimensions such as 1,440 by 900 and begin with scale 1.
  2. Navigate with a bounded timeout. Use networkidle0 only as one signal; applications that poll continuously may never reach it.
  3. Wait for the application’s ready condition. This might be a selector, a data attribute or a state exposed by your app. Do not substitute an arbitrary sleep for that condition.
  4. Wait for assets. Await document.fonts.ready and every required image’s load or error event. Add chart-specific readiness where applicable.
  5. Check geometry. Record document width and height and verify the element you expect to see has a non-zero bounding box.
  6. Capture a normal viewport image first. If it is already wrong, full-page mode is not the root cause.
  7. Capture full page at scale 1. Only after that succeeds should you test scale 2, export CSS or other changes.
  8. Use a narrower capture mode when appropriate. Choose an element screenshot for one component, a clip for a bounded region and PDF generation for paginated print output.

Runnable Puppeteer baseline

This Node.js example establishes a stable baseline. Replace the URL, ready selector and optional export class with values from your application.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();

  await page.setViewport({
    width: 1440,
    height: 900,
    deviceScaleFactor: 1
  });

  try {
    await page.goto('https://example.com/report', {
      waitUntil: 'networkidle0',
      timeout: 60_000
    });

    // Use a real application condition, not a guessed delay.
    await page.waitForSelector('[data-report-ready="true"]', {
      visible: true,
      timeout: 30_000
    });

    await page.evaluate(async () => {
      if (document.fonts?.ready) await document.fonts.ready;
      const images = [...document.images];
      await Promise.all(images.map(img => {
        if (img.complete) return Promise.resolve();
        return new Promise(resolve => {
          img.addEventListener('load', resolve, { once: true });
          img.addEventListener('error', resolve, { once: true });
        });
      }));
    });

    const metrics = await page.evaluate(() => {
      const root = document.documentElement;
      const body = document.body;
      const target = document.querySelector('[data-report-ready="true"]');
      const box = target?.getBoundingClientRect();
      return {
        scrollWidth: root.scrollWidth,
        scrollHeight: root.scrollHeight,
        bodyWidth: body?.scrollWidth ?? 0,
        bodyHeight: body?.scrollHeight ?? 0,
        targetWidth: box?.width ?? 0,
        targetHeight: box?.height ?? 0
      };
    });
    console.log(metrics);

    if (!metrics.targetWidth || !metrics.targetHeight) {
      throw new Error('Ready marker has zero geometry');
    }

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

Keep the first successful run intentionally simple. Add captureBeyondViewport: false only when you are investigating a resize or flash, and test an explicit clip if the page remains unstable. If your export class changes layout, apply it after the ready check and remove it in a finally block or before reusing the page.

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

Lazy-loaded images and infinite-scroll pages

Full-page mode captures what is present in the DOM. It does not trigger unbounded scrolling. For a feed or catalog, scroll in finite increments, wait for each batch, and stop when both document height and loaded-item count stop increasing. A bounded routine is safer than “scroll until nothing happens,” which can hang on pages with ads or polling.

async function loadFiniteContent(page, {
  itemSelector,
  maxPasses = 30,
  pauseMs = 300
}) {
  let previousHeight = 0;
  let previousCount = 0;

  for (let pass = 0; pass < maxPasses; pass++) {
    await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
    await new Promise(resolve => setTimeout(resolve, pauseMs));

    const state = await page.evaluate(selector => ({
      height: document.documentElement.scrollHeight,
      count: document.querySelectorAll(selector).length
    }), itemSelector);

    if (state.height === previousHeight && state.count === previousCount) break;
    previousHeight = state.height;
    previousCount = state.count;
  }

  await page.evaluate(() => window.scrollTo(0, 0));
}

// Example before the screenshot:
await loadFiniteContent(page, { itemSelector: '.catalog-card' });

After the routine, wait for the newly inserted images and any chart or component readiness signal, then capture. Set a maximum pass count and, where possible, an application-level “all results loaded” condition so a broken endpoint cannot create an endless job.

Match the fix to the symptom

Symptom Likely cause First corrective test
Entire image is white Scale-factor/rendering defect, blank page, or capture started before content existed Run at deviceScaleFactor: 1; verify URL, ready selector and non-zero geometry before capture
Right or bottom content is clipped Unexpected overflow, late layout changes or a target that was not fully present Log document dimensions; wait for assets; compare with a normal viewport screenshot
Width is different from the browser Full-page geometry changed vw, sticky or fixed layout Preserve the configured width where possible, test captureBeyondViewport: false, and use export CSS
Images are missing Lazy loading or image requests still pending Perform bounded scrolling, await image load/error, then verify image dimensions
Fonts or charts differ between runs Asynchronous rendering or animation Await document.fonts.ready, use a real chart-ready signal and disable animations for export
Header or modal appears repeatedly Sticky/fixed positioning in a tall capture Apply a temporary export class that restyles the element, then restore the page
Full page flashes while viewport capture works Capture-mode interaction or a Puppeteer/Chromium-specific issue Try captureBeyondViewport: false, an explicit clip or an element screenshot

When not to use fullPage

  • One component: use elementHandle.screenshot(). It avoids unrelated page height and fixed overlays.
  • A bounded region: use a clip with explicit x, y, width and height. This also makes output dimensions predictable.
  • Paginated documents: use PDF generation with paper size, margins and page ranges rather than forcing a single very tall bitmap.
  • Visual regression: freeze data, animations, time and responsive state; capture the same viewport and scale on every run. Compare dimensions before pixel diffs.

Performance, reliability and cost considerations

A full-page bitmap’s memory use grows with both width and height. Very tall pages are slower to rasterize and more likely to hit browser or image-size limits than an element or paginated export. Keep the viewport no wider than the requirement, split extremely long reports into sections when a single bitmap is not essential, and close pages and browsers in a finally block.

For repeatable CI captures, pin compatible Puppeteer and Chromium versions, log viewport settings and document metrics, and retain a failing screenshot plus console and page-error output. Treat timeouts, bot checks, blank documents and failed loads as distinct outcomes so a retry policy does not hide a deterministic application bug.

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

Or skip the browser setup

For an API-based route, ScreenshotNeo is the first option to try when you want a clean website capture: it removes cookie banners, newsletter popups and chat widgets before the shot, and only clean shots are billed. Its endpoint can return PNG, JPEG, WebP or PDF, supports full-page capture with lazy images loaded, CSS-selector element capture, waits, custom CSS and JavaScript, device presets or any viewport, retina scale, request blocking, headers, cookies, user agents, timezone and geolocation, and asynchronous jobs or bulk capture.

One GET request is enough. The documented API examples are below; see the ScreenshotNeo API documentation for parameters and response headers.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

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

Each response reports whether the page was clean and whether it was billed through X-Page-Verdict and X-Billed headers. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing. An MCP server provides take_screenshot, get_page_info and capture_pdf tools to Claude, Cursor and other MCP clients.

The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; Growth is $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. Start with the free ScreenshotNeo account.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

FAQ

What if my application keeps a network connection open?

Do not wait indefinitely for networkidle0. Use a selector or application state that means the specific report or component is ready, then perform the font, image and geometry checks.

Should I increase the viewport height to the document height?

Usually no. Keep a controlled viewport width and ordinary height, then let full-page capture include the document. Making the viewport itself extremely tall can introduce the same viewport-relative CSS problems you are trying to diagnose.

How do I test a scale-factor failure safely?

Save a known-good scale-1 capture first. Then test scale 2 with the same page state and compatible Puppeteer/Chromium versions; if it fails, retain scale 1 or investigate the page dimensions before changing other variables.

Frequently Asked Questions

What if my application keeps a network connection open?

Do not wait indefinitely for networkidle0. Use a selector or application state that means the specific report or component is ready, then perform the font, image and geometry checks.

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

Should I increase the viewport height to the document height?

Usually no. Keep a controlled viewport width and ordinary height, then let full-page capture include the document. Making the viewport itself extremely tall can introduce the same viewport-relative CSS problems you are trying to diagnose.

How do I test a scale-factor failure safely?

Save a known-good scale-1 capture first. Then test scale 2 with the same page state and compatible Puppeteer/Chromium versions; if it fails, retain scale 1 or investigate the page dimensions before changing other variables.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Crashes, No Sound, or Screen Glitches?Free driver scan
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.