How to Set Screen Size and Device Scale Factor with Puppeteer
Set Puppeteer’s page viewport and device scale factor with page.setViewport, or emulate a named device. Learn how viewport, window size, and screen dimensions differ.
Use await page.setViewport({ width, height, deviceScaleFactor }) to set a Puppeteer page’s viewport and device scale factor. Call it before page.goto() when the site needs to see those dimensions on its initial load. This configures the page viewport; it does not necessarily resize the outer browser window or change the available display.
1. Set a custom viewport and device scale factor
This runnable ES module example creates a page with a 390 by 844 CSS-pixel viewport and a device scale factor of 3, then captures a screenshot.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setViewport({
width: 390,
height: 844,
deviceScaleFactor: 3,
});
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'mobile.png' });
console.log(page.viewport());
} finally {
await browser.close();
}
Install Puppeteer in a project with npm install puppeteer, save the example as an .mjs file, and run it with Node.js. The call to setViewport returns a promise, so await it. Puppeteer documents width, height, and deviceScaleFactor as viewport settings. See the Page.setViewport API and screenshot guide.
What the values mean
widthandheightset the page viewport dimensions in CSS pixels.deviceScaleFactorsets the ratio between CSS pixels and device pixels used for rendering. A value of 1 uses one device pixel per CSS pixel; a larger value can produce a denser screenshot.- The viewport is per page. Configure each page whose layout or screenshot needs different dimensions.
Use dimensions that match the responsive layout you intend to inspect. Device scale factor is not a substitute for choosing the correct CSS viewport width: a high-density rendering at a desktop viewport width still exercises desktop responsive breakpoints.
2. Emulate a named device when user-agent behavior matters
If you need a known device’s viewport and user agent together, use Puppeteer’s exported KnownDevices entry and page.emulate(). Emulate before navigation so the initial request and page load use the device settings.
import puppeteer, { KnownDevices } from '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', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'iphone.png' });
} finally {
await browser.close();
}
Device presets are tied to the Puppeteer version installed in your project. Check the available entries in that version before relying on a particular name. page.emulate() applies the preset’s viewport and user agent; use setViewport() when you only need custom dimensions and scale. See Page.emulate and KnownDevices.
3. Choose the right size control
“Screen size” can refer to three different things. Pick the API based on what you want the page or browser to observe.
| What you need to control | Use | What it affects |
|---|---|---|
| Responsive page layout | page.setViewport({ width, height, deviceScaleFactor }) |
The page’s viewport settings. |
| A named device’s viewport and user agent | page.emulate(KnownDevices[...]) |
Device preset emulation, including viewport and user agent. |
| Browser window content dimensions or bounds | Page.resize and browser window bounds APIs |
Window content size or the outer window bounds and state. |
| Available screens in headless Chrome | --screen-info and the documented Browser.screens, Browser.addScreen, and Browser.removeScreen APIs |
Headless screen configuration and topology. |
setViewport() does not mean “resize the physical monitor.” Window content dimensions also differ from outer bounds because a browser window can include browser chrome. For details, see Puppeteer’s window management guide and screens guide.
Viewport versus browser window
For screenshots of a webpage or responsive layout checks, the page viewport is usually the relevant dimension. Use window management when your automation specifically depends on the browser window’s content area, bounds, or state. The window APIs describe different measurements, so do not assume setting a viewport also sets the outer window to the same width and height.
Viewport versus available screen
In headless Chrome, Puppeteer’s screen guide documents an 800 by 600 screen by default unless --window-size is specified, and documents --screen-info as headless-only. Headful Chrome uses the platform’s physical screens. Configure the screen when the behavior under test depends on available screen topology; configure the page viewport when it depends on the webpage’s layout area.
4. Defaults, reset behavior, and timing
Puppeteer’s ConnectOptions.defaultViewport defaults to { width: 800, height: 600 } and is applied to each page. That can explain why a new page appears constrained even when the script has not called setViewport().
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
defaultViewport: { width: 1280, height: 800 },
});
try {
const page = await browser.newPage();
console.log(page.viewport());
// Reset this page to the configured default viewport.
await page.setViewport(null);
} finally {
await browser.close();
}
page.setViewport(null) resets to the configured default viewport. It does not mean “use any physical screen size.” See ConnectOptions and the setViewport API.
Set viewport or device emulation before navigation when the site’s initial response or scripts depend on device metrics. Puppeteer cautions that some sites do not expect phones to change size, and changing certain mobile or touch settings can reload the page. Await viewport changes, and avoid changing these settings mid-run unless that reload or layout transition is part of what you are testing.
5. Protocol support and compatibility
Puppeteer’s current WebDriver BiDi guide documents Page.setViewport support for width, height, and deviceScaleFactor. It lists Page.emulate() and other emulation capabilities as unsupported over BiDi. If you use BiDi, check the guide for the exact capabilities available in your setup before depending on device presets or additional emulation flags. The available surface can differ by protocol and Puppeteer release. See the WebDriver BiDi guide.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The page still uses an 800 by 600 layout. | The configured default viewport is active, or the custom setting was not applied to this page. | Call and await page.setViewport() on the page you navigate, or set defaultViewport when launching Puppeteer. Inspect page.viewport() to see Puppeteer’s recorded setting. |
| The initial page load behaves like a different device. | Viewport or device emulation was applied after navigation. | Set the viewport or call page.emulate() before page.goto(), then reload if needed. |
| The outer browser window does not match the requested dimensions. | setViewport() controls page viewport settings, not necessarily the outer window bounds. |
Use Puppeteer’s window management APIs for content dimensions or bounds. |
| A named device entry is undefined or unavailable. | The preset name may not exist in the installed Puppeteer version. | Check that release’s KnownDevices entries, or specify a custom viewport directly. |
| Device emulation fails or options have no effect with BiDi. | The BiDi implementation supports a narrower emulation surface. | Check the BiDi guide. Its documented setViewport support is limited to width, height, and device scale factor; Page.emulate() is listed as unsupported. |
| The page reloads after a device setting changes. | Puppeteer warns that some mobile or touch setting changes can cause reloads. | Apply the final settings before navigation where possible, and make the reload part of the test flow if you intentionally change settings later. |
7. Performance, reliability, and cost
Viewport settings do not make navigation or rendering instantaneous: the page still has to load and render its content. Keep the viewport stable during a run, and wait for the page condition your task needs before taking a screenshot. networkidle2 is used in the examples as one navigation wait condition; pages with persistent network activity may need a different wait strategy, such as waiting for a selector or a known application-ready signal.
For repeatable automation, record the viewport dimensions, device scale factor, Puppeteer version, and protocol alongside results. Use a device preset when user-agent behavior is part of the requirement; use explicit dimensions when layout width is what matters. This makes comparisons easier to interpret when a dependency or preset changes.
Running Puppeteer means managing a browser process and its runtime environment. For one-off or service-side capture workloads, weigh that setup and browser operation against a screenshot API’s per-plan limits and options. ScreenshotNeo lists 1,000 screenshots per month free with no card, then paid plans from $5 for 3,000; see its website for the product and plans.
Or skip the browser setup
ScreenshotNeo accepts one GET request with a URL and returns a screenshot or PDF. Its API options include viewport dimensions and device presets; see the ScreenshotNeo API docs for supported parameters.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(async fs => {
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
});
ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Those features are available on every plan.
Sign up for 1,000 free screenshots a month with no card.
FAQ
Does deviceScaleFactor change the viewport width?
No. Width and height define the CSS viewport. Device scale factor sets the pixel density used for rendering.
Should I use setViewport or emulate?
Use setViewport() for custom viewport dimensions and scale. Use page.emulate(KnownDevices[...]) when you want a named device preset, including its user agent.
Does setViewport resize my monitor?
No. It configures page viewport settings. Browser window bounds and available screens are handled through separate controls.


