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.
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/finallyor 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.


