Fix Playwright Screenshots That Are Clipped at the Bottom of a Webpage
Fix Playwright screenshots clipped at the bottom: check full-page mode, clipping, scroll containers, and page stability, with runnable code and fixes.
If a Playwright screenshot is clipped at the bottom, first check whether the capture is only the viewport. A plain page.screenshot() captures the current viewport; request the full scrollable page with fullPage: true in JavaScript or full_page=True in Python. If the bottom is still missing, check for a clip rectangle, identify which element actually scrolls, and wait for the page’s content and layout to settle before capturing.
1. Request a full-page screenshot
Playwright’s documented default is viewport capture. Full-page mode asks Playwright to capture the full scrollable page, as though it had a very tall screen. Remove any unnecessary clip option while diagnosing.
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: 'domcontentloaded' });
await page.screenshot({ path: 'page.png', fullPage: true });
await browser.close();
})();
Run it with node screenshot.js after installing Playwright for Node.js and its browser. In Python, the equivalent is:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1280, "height": 800})
page.goto("https://example.com", wait_until="domcontentloaded")
page.screenshot(path="page.png", full_page=True)
browser.close()
Use the matching full_page spelling in Python. For a screenshot of just the visible viewport, leave full-page mode off.
2. Check for a clip rectangle
A screenshot call with clip asks for a bounded rectangle. Its x, y, width, and height determine the captured region, so a height that ends before the page bottom will crop the result even if the page itself is taller.
// Diagnostic: remove clip and capture the full scrollable page.
await page.screenshot({ path: 'page.png', fullPage: true });
// Intentional crop: this rectangle is bounded to the requested dimensions.
await page.screenshot({
path: 'section.png',
clip: { x: 0, y: 0, width: 900, height: 1200 }
});
If removing clip restores the bottom, inspect where the rectangle is calculated and whether it is based on stale or viewport-only dimensions. Keep a clip only when a crop is actually the desired output.
3. Find which element owns the scrollbar
Full-page capture addresses the document’s scrollable page. Some layouts put the content inside a nested panel with its own scrollbar, while the document itself stays short. In that case, inspect the page in a browser and determine whether the scrollbar belongs to the document or a particular element. A full-page screenshot of the document may not include content that only appears by scrolling that nested element.
For a specific element, Playwright supports element screenshots. Capture the element when its full rendered box is the target:
const panel = page.locator('.results-panel');
await panel.screenshot({ path: 'panel.png' });
An element screenshot does not automatically mean every off-screen item in an independently scrolling panel has been revealed. If the target content appears only as the panel scrolls, first reproduce the issue, identify that container, and choose a capture approach that exposes its full content. There is no universal nested-scroller setting: the right approach depends on the page’s DOM and layout.
4. Wait for stable content and layout
Late-loading content, expanding sections, and animation can change page height around capture time. Prefer waiting for a condition tied to the content you need rather than adding an arbitrary delay:
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('#main-content').waitFor({ state: 'visible' });
await page.screenshot({ path: 'page.png', fullPage: true });
For a page whose relevant content appears after a known application event, wait for that selector or state instead. A generic delay can mask a timing issue without ensuring the page is actually ready.
If motion is the cause, Playwright’s screenshot API provides an animations option; its default is allow. The screenshot API also supports a stylesheet to hide dynamic elements or adjust styles for repeatable captures. Use these to control known moving elements, not as a substitute for checking the screenshot geometry.
5. Keep pixel scale separate from capture area
The scale option controls output pixel density, not how far down the page the capture extends. scale: 'css' maps one screenshot pixel to one CSS pixel. scale: 'device' captures device pixels and can produce a larger image on a high-DPI setup.
await page.screenshot({
path: 'page.png',
fullPage: true,
scale: 'css'
});
If the screenshot has the expected content but its pixel dimensions differ from what you expected, check scale and device pixel ratio. If content is absent, return to capture area, clipping, scroll ownership, and readiness.
6. Troubleshooting checklist
| Symptom | Likely cause | What to do |
|---|---|---|
| Image ends at the viewport bottom | Viewport capture is the default | Set fullPage: true (JavaScript) or full_page=True (Python). |
| Image ends at a repeatable height despite full-page mode | A clip rectangle limits the output |
Remove clip to diagnose; otherwise increase its intended bounds. |
| The document screenshot omits content inside a panel | A nested element, rather than the document, owns the scrollbar | Inspect the DOM and scrollbar; target the relevant element or use a capture strategy for that container. |
| Bottom content appears inconsistently | Content or layout changes near screenshot time | Wait for a relevant selector or application state; control known animation if needed. |
| Image dimensions are unexpected but content is present | CSS-pixel versus device-pixel scale | Choose the intended scale; it does not extend the capture boundary. |
| A minimal full-page reproduction still captures only the viewport | Potential browser or Playwright-specific regression | Record Playwright version, browser, viewport, page structure, and a minimal reproduction before investigating a framework issue. |
A Playwright issue opened on June 9, 2022 described a full-page screenshot assertion capturing only the viewport with Playwright 1.22.2 and Chromium on Windows and Linux. That is a historical, version-specific report, not evidence of a general current bug. Reproduce with your installed version and browser before treating a regression as the cause.
7. Choose the right capture target
| Need | Capture choice | Check |
|---|---|---|
| What a user currently sees | Viewport screenshot | Expected viewport dimensions and readiness. |
| Whole document page | Full-page screenshot | Document is the scroll owner; no unintended clip. |
| One component or panel | Element screenshot | Element’s rendered bounds include the intended content. |
| A defined region | Clip rectangle | Coordinates and dimensions deliberately cover the target. |
Playwright’s documentation describes a full-page screenshot as the “full scrollable page.” That distinction matters for apps where scrolling happens inside a component rather than on the document.
8. Performance, reliability, and cost
Full-page captures can produce much taller and larger images than viewport captures, so image encoding and downstream storage or transfer may take more resources. Device-pixel scale can further increase pixel dimensions. Use viewport capture when the viewport is all you need, and use CSS scale when device-pixel detail is unnecessary. For repeatable output, fix the viewport, wait for a meaningful ready condition, and control known animation.
For reliability, keep a minimal reproduction with the same browser, viewport, page structure, and screenshot options. This separates a bad capture boundary from page-specific scrolling or timing behavior. The reviewed sources provide no universal performance benchmark or failure rate, so actual capture time and image size depend on the page and environment.
9. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its one-call endpoint returns an image or PDF. If you want to avoid setting up and maintaining a browser capture script, this is the API call:
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}`);
See the ScreenshotNeo API documentation for request options and setup. ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each cleanup step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with page verdict and billing details in response headers. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is on every plan.
Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card.
FAQ
Does full-page mode capture content inside every scrollable panel?
No assumption is safe: it requests the full scrollable document page. Inspect nested scroll containers and choose a target or strategy that exposes the panel content.
Will changing screenshot scale reveal the missing bottom?
No. Scale affects pixel density and output dimensions, not the capture boundary.
Should I add a long timeout before every screenshot?
Only if a known timing requirement calls for it. A selector or application-state wait tied to the needed content is more diagnostic than an arbitrary delay.
Is a Playwright bug the most likely explanation?
Start with options and page layout. The cited historical issue is specific to an older version and environment; verify a minimal reproduction on your current setup.


