How to Set Screenshot Viewport Size and Device Scale Factor in Puppeteer
Set Puppeteer’s CSS-pixel viewport and device scale factor before navigation, then choose whether to capture the viewport, full page, or a region.
Set the viewport with page.setViewport() before navigating, and pass deviceScaleFactor alongside the width and height. The dimensions are CSS pixels; the scale factor controls the emulated device pixel ratio used for rendering. Then capture the configured page with page.screenshot().
const page = await browser.newPage();
await page.setViewport({
width: 1280,
height: 720,
deviceScaleFactor: 2,
});
await page.goto('https://example.com');
await page.screenshot({ path: 'page.png' });
For example, a 1280 × 720 CSS-pixel viewport at scale factor 2 represents a higher-density rendering than the same viewport at factor 1. Set the viewport before page.goto() so the page’s initial layout uses the intended dimensions. See Puppeteer’s Viewport interface and Page.setViewport() documentation.
1. Install Puppeteer and create a browser page
Install Puppeteer in a Node.js project:
npm install puppeteer
Save the following as screenshot.js. It launches a browser, creates a page, sets the viewport and device scale factor, navigates, captures a PNG, and closes the browser even if a step fails.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setViewport({
width: 1280,
height: 720,
deviceScaleFactor: 2,
});
await page.goto('https://example.com', {
waitUntil: 'networkidle2',
timeout: 30000,
});
await page.screenshot({ path: 'page.png' });
} finally {
await browser.close();
}
})();
Run it with node screenshot.js. The example uses networkidle2 as a navigation condition; some sites keep network connections open, so a different wait condition or an explicit selector wait may suit them better.
2. Choose viewport dimensions and device scale factor
width and height describe the layout viewport in CSS pixels. They determine responsive breakpoints and the amount of page content visible in a viewport-sized screenshot. deviceScaleFactor sets the emulated device scale factor; Puppeteer documents its default as 1.
| Configuration | What it controls | When to use it |
|---|---|---|
width, height |
Viewport size in CSS pixels | Reproducing a desktop or mobile layout, or controlling the visible area |
deviceScaleFactor: 1 |
Default device scale factor | Ordinary screenshots when higher-density rendering is not needed |
deviceScaleFactor: 2 |
Higher-density emulation | Captures intended for high-DPI display or image inspection |
deviceScaleFactor: 0 |
Resets the scale factor to the system default | When you explicitly want Puppeteer to use that default |
Keep layout size and output density conceptually separate: changing the CSS viewport width can change the page layout, while changing the scale factor changes the emulated rendering density. If you need a particular responsive layout, set the desired CSS-pixel dimensions first and choose the density independently.
Puppeteer’s Viewport interface reference documents the viewport fields and defaults. The exact pixel dimensions of a resulting file can depend on capture extent and screenshot options, so choose and inspect the output format appropriate to your workflow.
3. Set the viewport before navigation
Configure the viewport before page.goto() when the first render must use the target dimensions. Puppeteer notes that changing viewport settings can cause a page reload in some cases, particularly when changing isMobile or hasTouch. Setting the intended configuration up front avoids an unnecessary second render and ensures the initial responsive layout is requested under the target settings.
const page = await browser.newPage();
await page.setViewport({
width: 390,
height: 844,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
});
await page.goto('https://example.com');
isMobile and hasTouch are optional viewport settings for mobile emulation. Use them when the page’s behavior should reflect mobile or touch conditions; they are not required just to set dimensions or scale factor. Consult Page.setViewport() for the current method behavior.
4. Select the screenshot area and output format
Viewport configuration and screenshot extent are separate choices. By default, page.screenshot() captures the visible viewport. Use fullPage: true for the full document, or clip to capture a rectangular region.
Viewport-sized screenshot
await page.screenshot({ path: 'viewport.png' });
Full-page screenshot
await page.screenshot({
path: 'full-page.png',
fullPage: true,
});
Full-page capture changes the capture extent; it does not replace the viewport dimensions used to lay out the page. For pages with lazy-loaded images or content that appears only after scrolling, make sure the content is loaded before capture. Puppeteer’s screenshot guide documents its screenshot workflow and element capture behavior: Screenshots.
Clipped region
await page.screenshot({
path: 'region.png',
clip: { x: 100, y: 80, width: 600, height: 400 },
});
Use coordinates and dimensions that describe the region you want to capture. Do not combine a full-page request with a clip unless you have checked the behavior against the Puppeteer version and capture you need.
Format, quality, and background
await page.screenshot({
path: 'page.webp',
type: 'webp',
quality: 85,
omitBackground: true,
});
pathsaves the file. Its extension determines the image type when specified; without a path, Puppeteer returns image data instead of saving to disk.typeselects a supported screenshot format. PNG is the documented default.qualityapplies to supported lossy formats and does not apply to PNG.omitBackgroundis false by default; set it to true when a transparent background is wanted and the page content supports it.fullPageis false by default.fromSurfaceis true by default.
See the ScreenshotOptions interface for the documented options and defaults.
Capture one element
When the target is a specific element rather than the viewport or whole page, use an element handle’s screenshot method:
const card = await page.waitForSelector('.product-card');
if (!card) throw new Error('Product card was not found');
await card.screenshot({ path: 'product-card.png' });
Puppeteer documents that element screenshot capture attempts to scroll a hidden element into view by default. See the Screenshots guide.
5. Set a default viewport for pages
For pages created through a browser connection, ConnectOptions.defaultViewport can set a shared default. Puppeteer documents a default viewport of 800 × 600 CSS pixels. Set it to null to disable that default viewport behavior.
const browser = await puppeteer.launch({
defaultViewport: {
width: 1440,
height: 900,
deviceScaleFactor: 1,
},
});
const page = await browser.newPage();
await page.goto('https://example.com');
Use a default when pages in the same browser should begin with the same dimensions. Use page.setViewport() for a one-off capture or when each page needs its own viewport. Puppeteer states that pages in one browser can have different viewports. Reference: ConnectOptions.
6. Python and cURL alternatives
Puppeteer is a Node.js library. If your task is simply to request a screenshot from an API rather than manage a local Puppeteer browser, these examples use ScreenshotNeo. The API accepts a URL and returns an image or PDF; see the ScreenshotNeo API documentation for supported parameters and formats.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-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"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
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 require('node:fs/promises').writeFile('shot.webp', image);
7. Or skip the browser setup
ScreenshotNeo takes a screenshot from one API request, without requiring you to launch and maintain a Puppeteer browser. It accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
For viewport and output options, see the ScreenshotNeo docs. One request looks like this:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
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 free and get 1,000 screenshots a month with no card.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The page has the wrong responsive layout | Viewport was set after navigation, or width and height were chosen in physical-pixel terms | Set width and height in CSS pixels before goto(). |
| The screenshot is not higher density | deviceScaleFactor was omitted, left at 1, or reset to 0 |
Set a positive factor such as 2 in the viewport object before navigation. |
| Changing mobile settings reloads the page | Changing isMobile or hasTouch can trigger reloads in some cases |
Set those options along with width, height, and scale before navigating. |
| Only the visible portion is in the file | Default capture is viewport-sized | Use fullPage: true, or use an element screenshot or clip for a narrower target. |
| The output is not saved where expected | path was omitted |
Provide a path, or handle the returned screenshot data in code. |
quality has no effect |
PNG is lossless and the quality option applies to supported lossy formats | Choose a supported lossy image type if adjustable quality is required. |
| Full-page output is missing lazy content | Some content has not loaded before the capture | Wait for the relevant selector or content to appear before taking the screenshot. |
| Navigation times out or never becomes idle | The site is slow or keeps network activity open | Choose an appropriate navigation wait condition and timeout, then wait for a specific selector when that better represents readiness. |
| Mobile behavior differs from a real device | Viewport emulation does not guarantee every device-specific behavior | Set the mobile and touch options you need and validate critical behavior on the target environment. |
9. Performance, reliability, and cost considerations
- Rendering work: Larger CSS viewports expose more content, and higher scale factors can increase the amount of image data produced. Use the dimensions and density required by the destination rather than increasing both without a reason.
- Full-page captures: Capturing a long document can involve substantially more content than a viewport-sized shot. Wait for required content, especially lazy-loaded images, before capture.
- Wait conditions: Network-idle waits can be unsuitable for pages with ongoing requests. A selector wait can make readiness criteria more specific.
- Browser lifecycle: Close the browser in a
finallyblock so errors during navigation or screenshot writing do not leave the process running. - Output size: PNG does not use the
qualityoption. A supported lossy format with an explicit quality may reduce file size when that tradeoff is acceptable. - Operating cost: Puppeteer runs in your environment, so account for the compute and maintenance of the browser process and any parallel captures. ScreenshotNeo offers 1,000 shots per month free with no card; paid plans begin at $5 for 3,000, with yearly billing offering two months free. Its billing headers distinguish clean captures from non-billable outcomes and cache hits.
10. Frequently asked questions
Does deviceScaleFactor change the CSS layout width?
The viewport width and height are CSS-pixel dimensions that determine layout. The device scale factor controls the emulated rendering density; specify both independently for predictable responsive captures.
Can different pages in the same browser use different viewport sizes?
Yes. Puppeteer documents that each page can have its own viewport. Set the dimensions on each page that needs a different capture configuration.
Can I capture an element without taking a full-page screenshot?
Yes. Find the element and call its screenshot() method. Puppeteer attempts to scroll an out-of-view element into view by default.
Where are the authoritative option references?
Use Puppeteer’s Viewport, Page.setViewport(), ScreenshotOptions, Screenshots guide, and ConnectOptions documentation.


