ScreenshotNeo

BlogHow-to

How to Fix Website Screenshots That Cut Off at the Bottom in Playwright

Use Playwright’s fullPage option for document screenshots, then check clipping, nested scroll areas, and viewport-constrained layouts if the bottom is still missing.

By the ScreenshotNeo team4 October 20266 min read

For a screenshot of the whole document, pass fullPage: true to Playwright’s screenshot method. It defaults to false, which captures only the visible viewport. If the bottom is still missing, check whether the content is in a nested scroll container, the page actually renders content below the fold, or a clip option limits the capture.

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

This guide uses Playwright’s JavaScript API. The same checks apply when diagnosing screenshots in other languages: first identify what area scrolls, then match the capture method to that area.

1. Capture the full document

Playwright’s fullPage option captures the full scrollable page instead of only the viewport. Set it on the screenshot call:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });

  await page.goto('https://example.com', { waitUntil: 'load' });
  await page.screenshot({ path: 'screenshot.png', fullPage: true });

  await browser.close();
})();

Install Playwright in your project with npm install playwright. If your project already uses Playwright Test, the same screenshot option works with its page fixture:

import { test, expect } from '@playwright/test';

test('captures the full document', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveScreenshot({ fullPage: true });
});

Use fullPage: true when the document itself is the target. It does not automatically expand every independently scrollable element inside the document.

2. Find what is being clipped

Work through these checks in order. They distinguish a screenshot option problem from a page-layout issue.

  1. Confirm the screenshot call. Verify it is the intended page and includes fullPage: true. Without it, Playwright’s documented default is a viewport screenshot.
  2. Check for clip. A clip rectangle has x, y, width, and height. If its bottom edge ends early, it restricts the capture area. Remove it for a full-document screenshot or adjust the bounds for the intended target.
  3. Identify the scrolling element. If the scrollbar belongs to a panel or another element rather than the document, the missing content is in a nested scroll container. Capture that locator directly.
  4. Check whether the page renders more content. A layout constrained to height: 100vh may have no content below the viewport. A screenshot cannot include content that the page does not render there.
  5. Use a larger viewport only as a diagnostic. Some viewport-constrained layouts behave differently at a taller viewport. Try it to investigate that page’s layout; it is not a universal fix.

3. Capture a nested scroll container

When the long content belongs to an independently scrollable element, screenshot the element itself:

await page.locator('.container').screenshot({ path: 'container.png' });

Replace .container with a selector for the actual scroll area. This captures the locator’s rendered bounds; it does not mean every nested scroll area is expanded automatically. Check the resulting image and the page’s scrolling behavior to confirm that the desired content is represented in those bounds.

4. Diagnose viewport-constrained layouts

If CSS or application logic limits the layout to the viewport, first check whether the supposedly missing content exists in the rendered page. A taller viewport can help determine whether viewport sizing is involved:

await page.setViewportSize({ width: 1280, height: 3000 });
await expect(page).toHaveScreenshot();

The 1280 × 3000 values are example dimensions from a reported issue, not a recommended standard. Whether a larger viewport changes the capture depends on the page structure. If the application only creates or displays the missing content after an interaction or scroll, reproduce that state before taking the screenshot.

5. A runnable diagnostic example

This example captures the full document, then captures a nested element separately. Adjust the URL and selector to match the page under investigation.

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });

  await page.goto('https://example.com', { waitUntil: 'load' });

  // Document-level capture
  await page.screenshot({ path: 'document-full.png', fullPage: true });

  // Element-level capture for a nested scroll area
  const panel = page.locator('.container');
  if (await panel.count()) {
    await panel.screenshot({ path: 'container.png' });
  }

  await browser.close();
})();

If the document image is still short, inspect the page’s rendered height and layout before changing screenshot dimensions. If the panel image is short, check whether the selector points to the intended element and how that element renders its scrollable content.

6. Troubleshooting

Symptom Likely cause What to do
The image ends at the viewport bottom fullPage was omitted; its default is false. Set fullPage: true on the document screenshot call.
The main page is full length, but a panel’s content is missing The content is inside a nested scroll container. Capture the target locator, such as page.locator('.container').screenshot().
The image ends at a precise boundary A clip rectangle may limit the screenshot. Inspect its y and height; remove or revise the clip as appropriate.
A larger viewport changes the result The layout may depend on viewport height or use a viewport-sized region. Inspect the page’s CSS and rendering rules. Treat viewport changes as diagnosis, not a general repair.
The expected content is absent even in a tall screenshot The page may not render that content in the current state. Check the application state and page structure; reproduce the state that displays the content before capture.
A locator screenshot targets the wrong area The selector may match a different or multiple elements. Use a selector for the actual scroll container and check the locator count and visible bounds.

7. Performance, reliability, and cost

Full-page images can be much taller than viewport images, so their output dimensions and file size can grow with document length. Capture only the area needed: the full document for a page-wide record, or a locator for a specific panel. For repeatable visual checks, keep the viewport and page state consistent, and ensure the content has reached the state you intend to capture.

Increasing viewport height can change responsive layout, so it may produce a different page rather than simply revealing more of the same page. It is useful for diagnosing viewport-dependent content, but should not replace checking the page’s actual scroll structure. Playwright screenshots have no per-shot service charge; account for the runtime and storage used by your own browser automation setup.

8. Or skip the browser setup

For a hosted page screenshot, ScreenshotNeo offers a one-request screenshot API. See the ScreenshotNeo API documentation for request options.

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));

ScreenshotNeo accepts cookie and consent banners like a visitor, then removes 60+ known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan.

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

9. FAQ

Does fullPage: true scroll through every panel on a page?

No. It captures the document’s scrollable content; nested scroll containers need to be handled as their own elements.

Should I always use a very tall viewport?

No. A tall viewport is a page-specific diagnostic and can change responsive layout. Start with the document’s scroll behavior and use the viewport test only to investigate viewport-dependent content.

Is a bottom cutoff always a Playwright bug?

No. The documented option, a clip rectangle, nested scrolling, and page layout can each explain the result. A historical issue report does not establish a general bug across Playwright versions.

What should I include in a bug report?

Provide a minimal reproduction, the screenshot call and options, the relevant scrolling or layout CSS, and the browser and Playwright version. That gives others the context needed to distinguish a capture setting from page behavior.

Sources