ScreenshotNeo

BlogHow-to

How to Capture a Full-Page Screenshot at a Specific Viewport Width

Set a browser viewport before navigation, then capture the full scrollable page. Includes Playwright, Puppeteer, scaling, dynamic content, and troubleshooting.

By the ScreenshotNeo team4 October 20267 min read

To capture a full-page screenshot at a specific viewport width, set the browser viewport width before navigating to the page, wait for the content you need to render, then take a screenshot with the framework’s full-page option. In Playwright, use page.setViewportSize({ width, height }) followed by page.screenshot({ fullPage: true }). The width controls the responsive layout in CSS pixels; full-page capture extends the image over the scrollable document.

Choose a practical viewport height as well as width. Set both dimensions before navigation so the site evaluates its responsive layout at the intended size. Framework documentation: Playwright Page API, Playwright Screenshots, and Puppeteer ScreenshotOptions.

1. Capture a full page with Playwright

This runnable Node.js example launches Chromium, chooses a 1280 by 800 CSS-pixel viewport, navigates, and writes a full-page PNG. Install Playwright with npm install playwright; install its browser with npx playwright install chromium.

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

(async () => {
  const browser = await chromium.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewportSize({ width: 1280, height: 800 });
    await page.goto('https://example.com', { waitUntil: 'load' });
    await page.screenshot({ path: 'full-page.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Replace the URL and dimensions for your target. The viewport width is the layout width in CSS pixels. Choose the width before goto; many sites are not designed to have their viewport changed after loading. A browser context can also define viewport options for pages created in it.

Wait for the content you need

waitUntil: 'load' waits for the load event, but it does not guarantee that client-rendered data, images loaded on scroll, or delayed animations have finished. If the page has a known ready selector, wait for it explicitly:

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.locator('[data-page-ready="true"]').waitFor({ state: 'visible' });
await page.screenshot({ path: 'full-page.png', fullPage: true });

Use a selector that genuinely indicates the content is ready. If the page has no such marker, a short fixed delay can help with known delayed content, but it is less reliable than waiting for a condition. For lazy images or content that appears only after scrolling or interaction, trigger the required loading behavior before capture and inspect the resulting image.

2. Capture with Puppeteer

Puppeteer provides the same basic sequence: set the viewport, navigate, then request a full-page screenshot. Install it with npm install puppeteer.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({ width: 1280, height: 800 });
    await page.goto('https://example.com', { waitUntil: 'load' });
    await page.screenshot({ path: 'full-page.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

For a particular application, choose based on the browser automation stack, language and runtime integration, and output behavior you need. Both Playwright and Puppeteer document viewport sizing and full-page screenshots; the sources cited here do not establish a performance ranking.

3. Understand viewport width, full-page capture, and output pixels

These settings answer different questions:

  • Viewport width and height: the browser’s visible layout area, measured in CSS pixels. Width can affect breakpoints, columns, navigation, and text wrapping.
  • Full-page capture: an instruction to capture the scrollable page beyond the visible viewport. It does not mean you made the viewport itself taller.
  • Clip rectangle: a bounded region with specific coordinates and dimensions. Use clipping when you want a region rather than the whole scrollable document.
  • Raster scale: the relationship between CSS pixels and output image pixels. In Playwright, scale: 'css' yields one image pixel per CSS pixel; scale: 'device' uses device pixels and can produce a larger image on high-density displays.

For example, a 1280 CSS-pixel viewport does not by itself guarantee a 1280-pixel-wide raster image if device-pixel scaling is used. To request CSS-pixel output in Playwright:

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

See the Playwright screenshot documentation for the scale option. For direct Chrome DevTools Protocol control, Page.captureScreenshot supports a clip region and captureBeyondViewport; its viewport geometry is in device-independent pixels. Framework wrappers can handle these details differently, so use their documented full-page option unless you need lower-level control. See the Chrome DevTools Protocol Page domain.

4. Handle dynamic and long pages

A full-page flag covers the scrollable document, but it cannot ensure that every page-specific element is in the intended state. Plan for content that loads only after scrolling, interaction, or time.

  1. Choose the target layout first. Set the desired viewport width and height before navigation.
  2. Wait for meaningful readiness. Prefer a page-specific selector or state over an arbitrary delay.
  3. Load scroll-triggered content if needed. Some lazy images and sections are requested only as they approach the viewport. Scroll through the page in steps when the page requires it, then wait for the relevant content before capture.
  4. Decide how animations should appear. A screenshot records a moment in time. If animation state matters, wait for a known state or disable animation with a page-specific style where appropriate.
  5. Review sticky and fixed elements. Their appearance during full-page capture can depend on browser behavior and page implementation. Check the output rather than assuming a sticky header will appear once or in a particular position.
  6. Check very long pages. Large documents can produce large images and take longer to render, transfer, and store. If you only need a section, use a clip instead of capturing the entire document.

Infinite-scroll pages have no stable, finite “whole page” until you define a stopping condition. Decide how many items or what endpoint counts as complete, load that content, and capture only then.

5. Troubleshooting

Symptom Likely cause Fix
The layout has the wrong breakpoint The viewport was changed after navigation, or the browser used a different width than intended. Set width and height before goto. Check the browser context configuration and use CSS-pixel dimensions.
The image shows only the visible screen The screenshot call omitted the full-page option. Use fullPage: true in Playwright or Puppeteer. Use a clip only when you want a bounded region.
Images or sections are missing Content is lazy-loaded, client-rendered, or appears after interaction. Wait for a meaningful selector; trigger the needed scroll or interaction; then capture and inspect the output.
The screenshot is larger or smaller than expected CSS viewport pixels and raster device pixels were confused, or scaling differs. In Playwright, choose scale: 'css' for one output pixel per CSS pixel, or use device scaling when that is the desired result.
Capture happens before the page is ready The selected navigation event does not represent completion of application data or delayed UI. Wait for the application’s ready state or target element. A fixed delay is a fallback, not proof that all content loaded.
Sticky elements look unexpected Fixed-position behavior during a full-document capture varies with implementation and capture details. Inspect a sample image, adjust page state or styling if appropriate, or capture a specific clip.
The process runs out of memory or takes too long The page is extremely tall, the image is high resolution, or many captures run at once. Capture only needed sections, use CSS scale if suitable, and limit concurrent browser jobs. Avoid keeping unnecessary browser pages open.

6. Reliability, performance, and cost

For repeatable captures, keep viewport dimensions, browser configuration, waits, and output scale explicit. Wait for a page-specific readiness condition where possible, and review captures of pages with lazy content, sticky elements, or animation. Neither a navigation event nor a full-page flag alone proves that all desired content has loaded.

Capture time and memory use depend on the page, output dimensions, browser, and concurrency. Full-page images can be much taller and larger than viewport images; use a clip if the whole document is unnecessary. The cited framework documentation describes controls, not comparative speed or cost benchmarks. A self-hosted browser workflow has the operational costs of running and maintaining its browser environment; estimate those for your own workload.

7. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a screenshot or PDF. For this task, pass the target URL and set the requested width with viewport_width; see the ScreenshotNeo documentation for parameter details. The example uses the product’s documented request shape; replace the URL and API key.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -d viewport_width=1280 \
  -d full_page=true \
  -o page.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": "YOUR_API_KEY",
        "url": "https://example.com",
        "viewport_width": 1280,
        "full_page": "true",
    },
    timeout=90,
)
r.raise_for_status()
open("page.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com',
  viewport_width: '1280',
  full_page: 'true'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('page.webp', bytes));

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and responses identify the page verdict and billing status in 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 screenshots.

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

8. FAQ

Should I set the viewport before or after navigation?

Before navigation. That lets the page render its responsive layout at the chosen width from the start.

Does full-page capture make the browser viewport taller?

No. It captures beyond the visible viewport to cover the scrollable document. The viewport dimensions still determine the responsive layout.

Will full-page capture include every lazy-loaded item?

Not necessarily. Load or trigger content that appears only after scrolling or interaction, then verify the image for the specific page.

How do I get an image whose pixel width equals my CSS viewport width?

In Playwright, set screenshot scale to 'css'. Device scaling can produce more raster pixels than CSS pixels.

What should I use if I only need part of a page?

Use a clip or capture the relevant element. Full-page mode is for the whole scrollable document.