ScreenshotNeo

BlogHow-to

How to Take a Puppeteer Screenshot at a Specific Viewport Size

Set Puppeteer’s viewport before navigation, then save a screenshot. Learn how to control dimensions, device scale, full-page output, and common capture issues.

By the ScreenshotNeo team4 October 20268 min read

Set the viewport before navigating, then call page.screenshot() with a file path. For example, a 1280 × 720 viewport at device scale factor 1:

const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 720, deviceScaleFactor: 1 });
await page.goto('https://example.com');
await page.screenshot({ path: 'screenshot.png' });

width and height are CSS pixels. Set them explicitly so the page uses the responsive layout you intend, rather than relying on a default. Puppeteer documents an 800 × 600 default viewport when configured through connection options. See the official viewport API and screenshots guide.

1. Install Puppeteer and capture a viewport screenshot

Use a current Node.js installation. Install Puppeteer in your project:

npm install puppeteer

Save this as screenshot.js and run it with node screenshot.js. Puppeteer downloads a compatible browser during installation unless your environment is configured to use another browser.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();

    // Set the viewport before navigation for predictable responsive layout.
    await page.setViewport({
      width: 1280,
      height: 720,
      deviceScaleFactor: 1,
    });

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

    await page.screenshot({ path: 'screenshot.png' });
    console.log('Saved screenshot.png');
  } finally {
    await browser.close();
  }
})();

The important order is setViewport(), then goto(), then screenshot(). Setting the viewport first lets the site respond to the intended dimensions during its initial layout and resource loading. Puppeteer notes that changing mobile or touch-related viewport settings can cause a page reload in some cases.

2. Choose the viewport dimensions and device scale

Pass integer CSS-pixel dimensions in width and height. Common examples include 375 × 812 for a narrow phone-sized layout, 768 × 1024 for a tablet-sized layout, and 1440 × 900 for a desktop layout. These are viewport examples, not guarantees about any particular device.

Setting What it controls When to set it
width, height Viewport dimensions in CSS pixels Always set both when you need reproducible responsive behavior.
deviceScaleFactor The device pixel ratio used for rendering Set to 1 for a standard-density output, or a higher value when you need more image pixels per CSS pixel.
isMobile Enables mobile emulation behavior Use when testing a mobile-specific rendering mode, along with the other relevant emulation settings.
hasTouch Enables touch-related behavior Use when the page behavior depends on touch input.

For a basic viewport screenshot, width and height are sufficient. Add deviceScaleFactor when the output’s pixel density matters. The viewport API lists the available viewport settings; changing isMobile or hasTouch may reload the page, so configure them before navigation when possible.

await page.setViewport({
  width: 390,
  height: 844,
  deviceScaleFactor: 2,
  isMobile: true,
  hasTouch: true,
});

A CSS viewport of 390 × 844 with device scale factor 2 can produce a raster image with roughly twice as many pixels in each dimension. That increases output dimensions and file size; it does not change the CSS layout width to 780 pixels.

3. Capture the viewport, the full page, or a region

By default, page.screenshot() captures the viewport. Use fullPage: true when you need the full document, including content below the fold. Use clip to capture a particular rectangle. These options answer different needs: viewport capture preserves the visible frame, full-page capture extends through the document, and clipping targets a defined area.

// Visible viewport only (the default)
await page.screenshot({ path: 'viewport.png' });

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

// A rectangle within the page, in CSS pixels
await page.screenshot({
  path: 'region.png',
  clip: { x: 100, y: 80, width: 600, height: 400 },
});

For full-page captures of long documents, confirm that lazy-loaded content has had a chance to appear before capturing. A viewport screenshot can be correct even when content farther down the document has not loaded. For clipping, ensure the requested rectangle makes sense for the page and the dimensions you configured.

4. Select image output and screenshot options

Puppeteer’s ScreenshotOptions reference documents the screenshot settings. The output type can be inferred from the filename extension. If you omit both an explicit type and a path extension, PNG is the default. If you omit path, Puppeteer returns image data instead of saving the screenshot to disk.

Option Purpose Notes
path Save the screenshot to a file Use a writable path. The extension can determine the output type.
type Choose png, jpeg, or webp, as supported by the installed Puppeteer version PNG is the documented default when the type is not inferred or specified.
quality Set lossy image quality for supported formats Relevant to JPEG and WebP, not PNG. Check the API reference for the version in your project.
fullPage Capture the whole document Defaults to false.
clip Capture a specified rectangular region Provide the region’s coordinates and dimensions.
omitBackground Omit the default background Useful when a transparent output is needed and supported by the chosen format.
await page.screenshot({
  path: 'page.webp',
  type: 'webp',
  quality: 80,
  fullPage: false,
});

Consult the API reference for option details that depend on your Puppeteer version. In particular, quality applies to lossy formats; use PNG when you need lossless output.

5. Wait for the page state you need

A screenshot records the page as it is when the capture happens. Navigation completion alone may not mean that a client-rendered component, image, or font is ready. Choose the wait condition to match the page, then add an explicit selector or application-specific readiness check if needed.

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('[data-ready="true"]', { timeout: 10000 });
await page.screenshot({ path: 'ready.png' });

For pages without a reliable readiness selector, a short delay can be a fallback, but it is less reliable than waiting for a condition tied to the content you need. Network-idle waits can also be unsuitable for pages that keep requests open or continuously poll. Select the wait condition based on the site’s behavior.

6. Save the screenshot as a buffer

When you omit path, page.screenshot() returns image data rather than writing a file. This is useful when the next step is uploading the image or processing it in memory.

const image = await page.screenshot({ type: 'png' });
// image is screenshot data; pass it to your storage or processing code.

If you need a file, include path. Make sure the destination directory exists and the process has permission to write there.

7. Use cURL, Python, or Node.js with ScreenshotNeo

If you want the same URL-to-image workflow without installing or managing a browser, ScreenshotNeo is a website screenshot API and MCP server for developers. The request below captures a page at a specified viewport size. See the ScreenshotNeo API documentation for the available parameters.

cURL

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

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": "YOUR_API_KEY",
        "url": "https://example.com",
        "width": 1280,
        "height": 720,
    },
    timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
    f.write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com',
  width: '1280',
  height: '720',
});
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 require('node:fs/promises').writeFile('shot.webp', image);

ScreenshotNeo also accepts parameters used by other screenshot APIs, which can make switching easier. Its options include full-page capture, device presets, retina scale, output formats, custom CSS and JavaScript, waits, and caching. The API can return PNG, JPEG, WebP, or PDF. Cookie and consent banners, newsletter popups, and chat widgets can be removed before capture; each cleanup step can be turned off. Responses indicate the page verdict and billing status in headers.

8. Troubleshooting viewport screenshots

Symptom Likely cause Fix
The page has the wrong responsive layout The viewport was set after navigation, or the dimensions were not set explicitly. Call setViewport() before goto() and specify both width and height.
The image dimensions are larger than expected deviceScaleFactor is greater than 1. Set it to 1 for a one-to-one CSS-pixel scale, or account for the higher raster dimensions.
The screenshot is blank or missing dynamic content The capture ran before the page or component finished rendering. Wait for a meaningful selector or app-ready condition before capturing.
networkidle never completes The page has long-running requests, polling, or other continuous network activity. Use a different navigation wait condition, then wait for the specific content needed.
The saved file is missing No path was supplied, or the destination cannot be written. Provide a path, create the directory, and check filesystem permissions. Without a path, consume the returned image data.
The file format is not what you expected The extension or screenshot type did not match the intended format. Set type explicitly and use a matching extension. PNG is the default when no type is inferred.
Mobile settings cause a reload or unexpected navigation Changing isMobile or hasTouch can reload the page in some cases. Set the full viewport configuration before navigation, then wait for the page state again.
Full-page output omits lower-page images Images may load lazily only when scrolled into view. Trigger the page’s lazy content to load and verify readiness before taking the full-page capture.

9. Performance, reliability, and cost

For repeat captures in a script, reuse a browser process and create pages as needed instead of launching a fresh browser for every URL. Always close pages and the browser when finished so the process does not accumulate resources. Large viewport dimensions, high device scale factors, full-page captures, and high-resolution assets increase memory use and output size.

Reliability depends on the target site as well as the script: pages can redirect, require authentication, block automation, or render content asynchronously. Set navigation timeouts, wait for the content you need, and handle errors so one failed capture does not silently produce a bad artifact. Capture only pages you are authorized to access.

Running Puppeteer yourself has no per-screenshot API charge, but it uses your compute, browser installation, and maintenance time. ScreenshotNeo’s listed plans are Free: 1,000 shots per month with no card; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; and Business: $249 for 1,000,000. Yearly billing gives two months free. Every feature is on every plan. Only clean shots are billed; bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Check the product site for current plan details.

Or skip the browser setup

Make one GET request to capture a URL at a 1280 × 720 viewport:

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

ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Read the API docs, then sign up for 1,000 free screenshots a month.

FAQ

Does Puppeteer use an 800 × 600 viewport by default?

The connection options reference documents 800 × 600 as the default viewport when configured through those options. Set your own dimensions explicitly for predictable results. See ConnectOptions.

Does fullPage: true change the viewport?

It changes the captured extent to include the full document; it is not a substitute for choosing the viewport dimensions used to lay out the page.

Can I take a screenshot without saving a file?

Yes. Omit path and use the returned image data in memory. The screenshot options reference documents the path behavior and supported options.

Where can I check the exact screenshot API behavior?

Use the official Page.screenshot() API reference, ScreenshotOptions reference, and Page.setViewport() reference for details relevant to your installed version.