ScreenshotNeo

BlogHow-to

Playwright Full-Page Screenshots Stop at the Viewport: How to Capture the Whole Page

Use Playwright’s full-page option to capture the entire scrollable page. Find the right fix for direct screenshots, visual assertions, pytest failure captures, and the CLI.

By the ScreenshotNeo team4 October 20266 min read

Playwright screenshots show only the visible viewport by default. For a direct JavaScript or TypeScript screenshot, set fullPage: true; in Python, use full_page=True. The same setting is not shared by every capture path: screenshot assertions, automatic pytest failure screenshots, and the Playwright CLI each have their own configuration.

1. Direct screenshots: set the full-page option

For JavaScript and TypeScript, pass fullPage: true to page.screenshot(). The page should be loaded and in the state you want to capture before calling it.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });

try {
  await page.goto('https://example.com', { waitUntil: 'load' });
  await page.screenshot({ path: 'full-page.png', fullPage: true });
} finally {
  await browser.close();
}

For Python, the parameter is spelled with an underscore. This synchronous example uses Playwright’s synchronous API:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={"width": 1440, "height": 900})
    try:
        page.goto("https://example.com", wait_until="load")
        page.screenshot(path="full-page.png", full_page=True)
    finally:
        browser.close()

In async Python, await the screenshot call and use the async Playwright API:

import asyncio
from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page(viewport={"width": 1440, "height": 900})
        try:
            await page.goto("https://example.com", wait_until="load")
            await page.screenshot(path="full-page.png", full_page=True)
        finally:
            await browser.close()

asyncio.run(main())

Full-page mode captures the full scrollable page extent instead of just the current viewport. It does not guarantee that the page has fetched or rendered every section. Lazy images, infinite lists, and content revealed by interaction may need additional handling before the capture.

2. Playwright Test screenshot assertions

If the screenshot is produced by a visual assertion, pass the full-page option to toHaveScreenshot. The assertion’s screenshot comparison waits for two consecutive screenshots to match before comparing against the expectation. That settling behavior helps with comparison stability; it is separate from choosing the screenshot extent.

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

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

If a test is still failing because the image differs, first check whether the expected image was created with the same page state, viewport, and full-page setting. Full-page capture fixes the extent; it does not make dynamic content identical between runs.

3. Automatic pytest failure screenshots

For automatic screenshots from the Playwright Test pytest plugin, enable screenshot capture and full-page screenshots together. The plugin’s automatic screenshots are viewport-only by default, and its full-page flag requires screenshot capture to be enabled.

# pytest.ini
[pytest]
addopts = --screenshot only-on-failure --full-page-screenshot

Use the plugin’s documented option names and check the configuration reference for the installed plugin version. If screenshots are not being written, verify that screenshot mode is enabled as well as the full-page flag.

4. Playwright CLI screenshots

For the CLI, pass --full-page to the screenshot command:

npx playwright screenshot --full-page https://example.com full-page.png

The CLI’s --hires option controls device-pixel output. It does not enable full-page capture; use --full-page for that.

5. Check what “whole page” means for your page

Full-page mode means the full scrollable page extent. It does not mean Playwright will universally scroll through the page and activate every lazy loader or interaction. If the lower part of your image is blank or missing content, investigate page loading separately from screenshot extent.

  1. Identify the capture path. Is this a direct page.screenshot(), a toHaveScreenshot assertion, a pytest failure artifact, or a CLI screenshot? Configure the option for that path.
  2. Set the full-page option. Use fullPage: true in JavaScript or TypeScript, full_page=True in Python, the pytest plugin’s full-page flag, or the CLI’s --full-page.
  3. Check when content appears. If a section loads after scrolling, clicking, or a network response, reproduce that page behavior and wait for the content before capture.
  4. Keep output settings separate. Image type, path, clipping, scale, animation handling, and caret visibility affect output or reproducibility. They do not replace the full-page option.
Capture path Full-page setting What to check
Direct JavaScript or TypeScript API fullPage: true Pass it to page.screenshot().
Direct Python API full_page=True Use the parameter on page.screenshot().
Playwright Test assertion { fullPage: true } Set it on toHaveScreenshot; allow for assertion settling.
Pytest automatic failure capture --full-page-screenshot Enable screenshot mode too.
Playwright CLI --full-page Do not confuse it with --hires.

6. Troubleshooting

The image ends at the viewport

Cause: the capture path is using its default viewport capture, or its full-page option was set in the wrong place. Fix: confirm which path produced the file, then set the corresponding option from the table above. For pytest automatic failure captures, enable both screenshot mode and the full-page flag.

The screenshot is full height, but lower sections are blank

Cause: the page may populate those sections only after scrolling, interaction, or a later response. The full-page option controls capture extent; it is not a universal lazy-loading trigger. Fix: reproduce the relevant page behavior, wait for the expected content or response, then capture.

The screenshot assertion still fails

Cause: full-page mode does not eliminate differences in page state. Also, toHaveScreenshot waits for consecutive screenshots to match before comparison. Fix: inspect the actual and expected images, stabilize the content and setup, and ensure the assertion uses the intended full-page setting.

The image has more pixels but still shows only the viewport

Cause: a high-resolution or device-scale option changes pixel dimensions, not capture extent. Fix: enable the full-page setting as well.

The pytest screenshot is not full-page

Cause: the full-page flag may be missing, screenshot capture may be disabled, or the installed plugin may use different options. Fix: enable both settings and check the plugin documentation that matches the installed version.

7. Performance, reliability, and output choices

A full-page image covers more content than a viewport image, so its pixel dimensions and output size can be larger. Choose a viewport that matches the rendering you need, and choose the image type and scale for the intended use. Scale and image format affect output characteristics, but neither changes whether the page extent is full-page.

For repeatable captures, keep the viewport, page state, and capture options consistent. Wait for content that matters to finish rendering. If the page uses animation or a moving caret, the screenshot API offers controls for those behaviors; use them when they are relevant to your comparison. Match examples and configuration to the installed Playwright and pytest plugin versions.

8. Or skip the browser setup

If you need a clean page image without managing a Playwright browser, ScreenshotNeo is a website screenshot API and MCP server. Its one-call API accepts a URL and returns an image or PDF. See the ScreenshotNeo API documentation.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month, with no card required.

9. FAQ

Does full-page mode capture an entire long page as one image?

It captures the full scrollable page extent. The page still needs to have rendered the content you want included.

Does --hires make a CLI screenshot full-page?

No. Use --full-page for the full scrollable page; --hires affects device-pixel output.

Does the same setting work in Python and JavaScript?

The API option has language-specific spelling: fullPage: true in JavaScript or TypeScript and full_page=True in Python.

Sources