ScreenshotNeo

BlogHow-to

How to Set the Puppeteer Viewport for Screenshots

Set Puppeteer’s viewport before navigation to capture the right layout and dimensions. Learn device emulation, full-page screenshots, and common fixes.

By the ScreenshotNeo team4 October 20269 min read

Set the viewport with page.setViewport() before navigating, then call page.screenshot(). The viewport width and height are measured in CSS pixels; use deviceScaleFactor to control the screenshot’s pixel density.

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setViewport({
    width: 1280,
    height: 800,
    deviceScaleFactor: 1,
  });
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'page.png' });
} finally {
  await browser.close();
}

Here, the visible page area is 1280 × 800 CSS pixels. A device scale factor of 1 produces roughly one output pixel per CSS pixel; a factor of 2 produces a denser image. For full-document capture, add fullPage: true to the screenshot options.

1. Install Puppeteer and run the basic screenshot

This example uses Puppeteer’s bundled browser. Run it in a project with Node.js installed:

npm install puppeteer

Save the following as screenshot.js and run node screenshot.js:

const puppeteer = require('puppeteer');

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

    // Set the intended CSS viewport before loading the site.
    await page.setViewport({
      width: 1280,
      height: 800,
      deviceScaleFactor: 1,
    });

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

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

For an ES module project, the equivalent import is import puppeteer from 'puppeteer';. Keep the ordering: create the page, await setViewport(), navigate, wait for page-specific readiness if needed, and capture.

2. Choose viewport dimensions and device settings

The viewport describes the browser page’s emulated visible area, not the operating system window. Choose dimensions that match the layout you want to inspect or render. Responsive breakpoints generally respond to CSS viewport width, so a width near a breakpoint can produce a different layout from one just above or below it.

Setting What it controls When to use it
width, height Viewport dimensions in CSS pixels Desktop layout, mobile layout, or a specific responsive breakpoint
deviceScaleFactor Pixel density for rendered output Use 1 for normal density; use a higher value when you need a denser image
isMobile Enables mobile viewport behavior, including honoring the page’s meta viewport tag When the page should behave as a mobile page, not merely have a narrow width
hasTouch Whether the emulated viewport supports touch events When page behavior depends on touch capability
isLandscape Sets landscape orientation in mobile emulation When matching a landscape device configuration

width and height are required when passing a viewport object. deviceScaleFactor defaults to 1; the other optional flags default to false. In the documented API, setting width, height, or deviceScaleFactor to 0 resets that property to its system default. page.setViewport(null) resets the viewport to its default.

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

Set the viewport before page.goto(). Puppeteer notes that some sites do not expect the page to change size after loading; changing mobile-related metrics can also trigger a reload in some cases. Each page can have its own viewport.

3. Capture the visible viewport or the full page

Changing viewport height does not itself request a full-document screenshot. By default, fullPage is false and the screenshot covers the visible viewport. Set it to true to capture the full page:

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

A very long page can create a large image and take longer to encode or write. If you need a specific region instead, use clip:

await page.screenshot({
  path: 'region.png',
  clip: {
    x: 0,
    y: 0,
    width: 640,
    height: 400,
  },
});

captureBeyondViewport controls capture outside the viewport in relevant cases. Its documented default is false when no clip is supplied and true when a clip is supplied. For a straightforward full-page image, use fullPage: true; for a precise crop, use clip and set beyond-viewport behavior if your use case requires it.

4. Emulate a known device when user agent matters

A narrow viewport alone does not make the browser behave exactly like a phone. When the user agent and device metrics both matter, use Puppeteer’s known-device emulation. Device names and metrics can vary between Puppeteer releases, so check the devices available in the version installed in your project.

const { KnownDevices } = require('puppeteer');
const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.emulate(KnownDevices['iPhone 17 Pro']);
  await page.goto('https://example.com');
  await page.screenshot({ path: 'device.png' });
} finally {
  await browser.close();
}

page.emulate() is a shortcut that applies a device user agent and viewport settings. Call it before navigation. Use setViewport() directly when you only need custom dimensions or do not need the device’s user agent.

5. Configure screenshot output

Viewport settings determine the rendered page area. Screenshot options determine which area is captured and how the image is saved.

Option Effect Notes
path Saves the image to a file If omitted, the screenshot data is returned rather than saved to disk. The file extension can determine the image type.
type Selects PNG, JPEG, or WebP where supported by the installed version PNG is the default.
quality Sets lossy image quality from 0 to 100 Applies to supported lossy formats, not PNG.
fullPage Captures the entire page Defaults to false.
clip Captures a specified rectangle Use x, y, width, and height to define the region.
omitBackground Hides the default white background Use when a transparent screenshot is needed.
await page.screenshot({
  path: 'page.webp',
  type: 'webp',
  quality: 85,
  omitBackground: true,
});

Transparency and lossy image formats serve different needs; choose output options based on how the image will be consumed. Confirm supported formats and option details against the Puppeteer version used by your application.

6. Wait for the page to be ready

Navigation completion does not guarantee every image, font, animation, or client-rendered component is visually ready. Puppeteer’s screenshot guide shows networkidle2 as one possible navigation wait, but network idleness is not a universal readiness signal. Use a selector or a page-specific condition when the content you need appears after navigation.

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

For a simple fixed delay, use await new Promise(resolve => setTimeout(resolve, 1000)); only when the page has no better readiness signal. A fixed delay can waste time on fast pages and still be too short on slow ones.

For a specific element, Puppeteer also supports capturing an element handle. The documented behavior attempts to scroll a hidden element into view before capture:

const card = await page.waitForSelector('.product-card');
await card.screenshot({ path: 'product-card.png' });

7. Set a default viewport for pages

If every page in a connected browser should start at the same dimensions, configure defaultViewport in the browser connection options. The documented default is 800 × 600. A per-page call to setViewport() can still be used when a particular screenshot needs different dimensions.

const browser = await puppeteer.launch({
  defaultViewport: {
    width: 1440,
    height: 900,
    deviceScaleFactor: 1,
  },
});
try {
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: 'default-viewport.png' });
} finally {
  await browser.close();
}

page.viewport() reports the configured viewport setting. The API documentation cautions that it does not inspect the page’s actual viewport, so treat it as configuration information rather than a measurement of rendered content.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; for example, this cURL request saves a WebP screenshot:

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

See the ScreenshotNeo API documentation for request parameters, including viewport options. Python and Node.js equivalents:

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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners are accepted and removed before capture; known consent platforms, newsletter popups, and chat widgets are removed. Each cleanup step can be turned off.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Response headers identify the page verdict and billing status.
  • An MCP server gives AI agents tools to take screenshots, inspect 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 screenshots; every feature is available on every plan.

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

9. Performance, reliability, and cost considerations

  • Use the smallest useful viewport. Larger dimensions and high device scale factors produce more image pixels, which can increase image size and capture processing.
  • Reserve full-page capture for when you need it. Very long documents create taller images and may require more memory and encoding time than viewport-only captures.
  • Wait for meaningful readiness. Prefer a selector or app-specific ready condition to long arbitrary delays. Network-idle waits can be unsuitable for pages with persistent requests.
  • Close browser resources. Use try/finally or equivalent cleanup so the browser closes after success or failure. Reuse browser processes where appropriate in a service, while managing page lifecycle and isolation for each capture.
  • Expect page-specific variation. Third-party content, animations, responsive breakpoints, and lazy loading can make captures differ. For full-page pages with lazy-loaded content, scrolling or app-specific loading may be required before capture.
  • Puppeteer costs are infrastructure costs. Puppeteer itself is a browser automation library; budget for the compute, memory, storage, and operational work of running browser instances. No general capture cost or speed benchmark applies to every page or deployment.

10. Troubleshooting

Symptom Likely cause Fix
The screenshot has the old or unexpected layout The viewport was set after navigation, or the page reacted to a later resize Set and await the viewport before goto(); check the target width against responsive breakpoints.
The image has fewer pixels than expected CSS pixels were mistaken for physical output pixels, or device scale factor is 1 Set an appropriate deviceScaleFactor, then account for the resulting larger image.
The mobile page still looks like desktop A narrow viewport alone does not enable all mobile behavior Try isMobile: true, include the intended meta viewport behavior, or emulate a known device when the user agent also matters.
The screenshot only contains the visible area fullPage defaults to false Set fullPage: true for a full-document capture.
Images or content are missing Capture happened before lazy content or client rendering was ready Wait for a relevant selector or application readiness condition; scroll or trigger lazy loading when necessary.
Navigation or capture hangs The page never reaches the selected navigation condition, or it has persistent network activity Choose a navigation wait suited to the site, use a timeout, then wait for the specific content needed rather than requiring network idleness.
A transparent screenshot has a white background The default background was included Set omitBackground: true and use an output format and consumer that preserve transparency.
Changing viewport reloads the page Some mobile viewport changes can trigger a reload Set the viewport or emulate the device before navigation.
A device name is undefined The installed Puppeteer release does not include that device name Check the known-device entries available in that installed release or specify the viewport and user agent directly.

11. Frequently asked questions

Does setViewport() resize the browser window?

No. It configures the page’s emulated viewport. Puppeteer has separate window-management APIs; most screenshot layout tasks need the page viewport setting.

Can each tab have a different viewport?

Yes. A viewport is configured per page, so pages in the same browser can use different dimensions.

How do I restore Puppeteer’s default viewport?

Call await page.setViewport(null). You can also create pages with a chosen default using the browser’s defaultViewport option.

Should I use a device preset or custom dimensions?

Use custom dimensions when the responsive width and height are what matter. Use a device preset when you also need that device’s emulated metrics and user agent.

Which Puppeteer version does this cover?

The referenced documentation is Puppeteer 25.12.0, retrieved October 3, 2026. Check the API documentation for your installed release if an option or device preset differs.

Sources