ScreenshotNeo

BlogHow-to

Playwright Full-Page Screenshot Blank Below the Fold: Fixes

A full-page screenshot captures the page’s scrollable extent, but it cannot capture content that has not loaded. Diagnose missing content, trigger lazy loading, and wait for the right signal.

By the ScreenshotNeo team4 October 20267 min read

If a Playwright full-page screenshot is blank below the fold, first confirm that the screenshot call uses fullPage: true. Then check whether the missing content exists in the page at capture time. Full-page capture extends the image to the page’s scrollable height; it does not fetch or render application content that only appears after scrolling.

For Python, use full_page=True. If the page loads content on scroll, trigger that behavior and wait for a specific application signal or target element before capturing. There is no universal sleep duration that guarantees every site is ready.

1. Confirm the screenshot captures the full page

Playwright captures the current viewport by default. Set the full-page option in the call that produces the file you are inspecting:

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

This changes the screenshot extent to the full scrollable page. It does not guarantee that scroll-triggered content has been requested, inserted into the DOM, or painted. Check that a later screenshot call is not overwriting the output file with a viewport-only capture.

In Python, the equivalent option is full_page=True; see the Playwright screenshots guide. The JavaScript Page API documents fullPage as capturing the full scrollable page instead of the visible viewport.

2. Find out whether the content exists before capture

Separate two different problems:

  • Content is missing from the DOM: the application has not loaded or created it yet. Fix the trigger or wait condition.
  • Content exists but is missing from the pixels: inspect layout, clipping, visibility, overlays, and the capture surface.

Check a stable locator for the expected lower content and inspect the document height before taking the screenshot. For example:

const expected = page.getByTestId('last-row');
console.log('document height:', await page.evaluate(() => document.documentElement.scrollHeight));
console.log('last row count:', await expected.count());

Use selectors from your own application. A locator count can show whether an element is present, but it does not prove that the element is visible or painted. Wait for the state you actually need, then compare a viewport screenshot, a full-page screenshot, and—if useful—a screenshot of the target element.

3. Trigger content that loads when you scroll

Lazy images, infinite lists, and other scroll-driven interfaces may not load below-the-fold content until a real scrolling interaction happens. Scroll a meaningful lower target into view, then wait for the expected content or the application’s loading signal.

const footer = page.getByText('Footer text');
await footer.scrollIntoViewIfNeeded();
await page.getByText('Last expected row').waitFor({ state: 'visible' });
await page.screenshot({ path: 'full.png', fullPage: true });

Replace the example text with stable locators. Playwright’s scrolling guide describes scrolling a footer into view as a way to force an infinite list to load more content. For lists that load in several batches, repeat the interaction and wait for each concrete batch or application signal. Scrolling once may not reach the end.

Prefer a state-based wait, such as a result row becoming visible, a loading indicator disappearing, or a known item count being reached. A fixed timeout can be useful for a known animation or unavoidable delay, but it is not a reliable substitute for knowing that the required content is ready.

4. Check nested scroll containers and rendering

Some pages scroll inside a panel rather than the document. If the missing section belongs to an inner container, scroll an element inside that container and verify that the container reveals or loads the content. A full-page screenshot of the document does not necessarily represent the full contents of a separately scrolling panel as a tall page.

If the content is present but still absent from the screenshot, inspect these possibilities:

  • Visibility and dimensions: check computed visibility, opacity, and the element’s bounding box.
  • Clipping and overflow: look for an ancestor with constrained dimensions and overflow: hidden or a nested scrolling surface.
  • Overlays: check whether a fixed banner or modal covers the content.
  • Layout position: check whether CSS positions content outside the document’s captured area.
  • Images and other deferred assets: verify that they have loaded and that their dimensions are nonzero before capture.

These are diagnostic checks, not a confirmed explanation for every blank screenshot. The title alone does not identify a particular site, browser, or failure cause.

5. Stabilize screenshots used in visual tests

If the problem appears only in screenshot assertions, make the test environment and page state repeatable. Playwright’s expect(page).toHaveScreenshot() waits for two consecutive screenshots to match before comparing against the baseline. That helps with transient changes, but it cannot make missing application content appear.

Rendering can vary with operating system, browser version, settings, hardware, power source, and headless mode. Keep those conditions consistent when investigating screenshot differences. Screenshot options can disable or control animations and inject styles; ensure that any stabilization style does not hide the content under test. See Playwright’s visual comparisons guide and the Page API.

6. Troubleshooting checklist

Symptom Likely cause What to check or change
Only the viewport is in the image The capture call omitted the full-page option, or another call overwrote the file. Use fullPage: true in the producing call and inspect the final output path.
The page is tall, but lower sections are empty Content is created only after scrolling or is still loading. Scroll a meaningful target into view and wait for expected content or an application-specific ready signal.
The expected locator has no matches The content has not been inserted, or the selector is wrong. Verify the selector and the page’s loading state before capture. Screenshot options cannot create missing DOM content.
The locator exists, but its screenshot area is blank The element may be hidden, zero-sized, clipped, covered, or outside the captured layout. Inspect its bounding box, visibility, ancestors’ overflow, overlays, and computed styles.
Scrolling the page does not load the section The page uses a nested scroll container, or more than one scroll increment is needed. Scroll within the relevant container, repeat the interaction as needed, and wait for newly revealed content.
Images are blank while text appears Deferred image loading or image requests may not have completed. Trigger loading by scrolling and wait for the relevant images or application readiness condition.
Visual test diffs vary between runs Rendering conditions or page state differ between captures. Keep browser, operating system, headless settings, and page state consistent; stabilize animations without hiding the target.

7. Performance, reliability, and cost

Full-page images include more pixels than viewport screenshots, so very tall pages can take longer to capture and produce larger output files. Infinite lists can also continue growing as they load, so define what “complete” means for the test instead of scrolling indefinitely. For repeatable results, trigger only the interactions needed to expose the target content and wait for a specific condition.

There is no single reliable wait time for all sites: network behavior and application logic vary. A fixed delay can add unnecessary runtime while still missing slow content. A concrete locator or application signal makes the capture’s readiness condition clearer. Keep browser and rendering conditions consistent for visual comparisons.

Playwright runs the capture in your browser automation environment; the supplied research does not establish a universal cost or performance figure. Resource use depends on page size, browser setup, and the number of captures.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the page verdict and billing status in headers. AI agents can use its MCP server tools: take_screenshot, get_page_info, and capture_pdf.

See the ScreenshotNeo API documentation. This example requests a WebP capture of Stripe:

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo offers 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000. Every feature is available on every plan. Sign up for free and get 1,000 screenshots a month with no card.

FAQ

Does fullPage: true automatically scroll the page?

It requests a capture of the full scrollable page. It does not ensure that the application’s scroll-triggered content has loaded; trigger and wait for that content first.

Should I use a fixed timeout before every screenshot?

Usually, wait for the content or application state you need. A timeout alone does not tell you that a lazy section finished loading.

Why does the document height look correct if the bottom is blank?

Document height describes layout extent, not whether every section contains visible, rendered content. Check the target locator, visibility, and scroll behavior as separate signals.

Can a full-page screenshot include an infinite list all the way to its end?

Only if the application has loaded the desired items before capture. Trigger the list’s loading behavior and decide on a clear stopping condition, such as a known final item or expected count.