ScreenshotNeo

BlogHow-to

How to Set Device Pixel Ratio for Mobile Website Screenshots in Puppeteer

Set Puppeteer’s deviceScaleFactor before navigation to control emulated pixel density in mobile screenshots. Compare manual viewport settings with device profiles and troubleshoot common capture issues.

By the ScreenshotNeo team4 October 20267 min read

Set deviceScaleFactor in the viewport configuration before navigating, then capture the page with page.screenshot(). This controls the emulated device pixel ratio (DPR) for the page. For a named device profile, use Puppeteer’s KnownDevices and page.emulate() before navigation.

The dimensions and scale factor below are examples, not a recommendation for a particular handset. Check the Puppeteer Viewport type for fields supported by the version you use.

1. Set a manual mobile viewport and DPR

Install Puppeteer in a Node.js project, save this as screenshot.mjs, and run it with node screenshot.mjs. The example saves a viewport screenshot as mobile.png.

import puppeteer from 'puppeteer';

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

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

  await page.goto('https://example.com', {waitUntil: 'networkidle2'});
  await page.screenshot({path: 'mobile.png'});
} finally {
  await browser.close();
}

Here, width and height describe the emulated viewport in CSS pixels, while deviceScaleFactor sets its pixel density. A higher factor can produce more image pixels for the same CSS viewport, but it does not change the CSS layout width to match a different phone. Choose viewport dimensions, DPR, and mobile behavior to suit the scenario you need to reproduce.

Apply viewport settings before navigation. Puppeteer warns that changing the viewport resizes the page, and changing mobile or touch properties can reload it. Some sites do not handle a phone-sized resize after page load as expected. See the Page.setViewport() documentation.

2. Use a bundled device profile

When you want Puppeteer’s bundled viewport metrics and user agent for a named device, emulate its profile before loading the site:

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-profile.png'});
} finally {
  await browser.close();
}

page.emulate() is a shortcut for applying a user agent and viewport settings. The device list can change between Puppeteer versions, so check the KnownDevices API reference for the profile available in your installed version. A profile is convenient when its bundled settings are appropriate; manual viewport configuration gives direct control over width, height, and scale.

3. Choose what the screenshot captures

DPR and screenshot region are separate controls. Use fullPage to capture the full document, clip to capture a defined region, and captureBeyondViewport where you need control over capture beyond the viewport. These options do not set DPR.

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

// A region expressed in page coordinates
await page.screenshot({
  path: 'region.png',
  clip: {x: 0, y: 0, width: 390, height: 300},
});

// Capture outside the viewport in a case that needs it
await page.screenshot({
  path: 'beyond-viewport.png',
  captureBeyondViewport: true,
});

Puppeteer infers screenshot type from the filename extension; PNG is the default when no type is specified. JPEG quality applies to formats that support it, not PNG. Review the ScreenshotOptions API for supported options and types.

4. Select the right emulation approach

Need Approach What to consider
Control specific CSS viewport dimensions and DPR page.setViewport() Set width, height, deviceScaleFactor, and mobile or touch flags where supported.
Approximate a named device profile page.emulate(KnownDevices[name]) Applies device metrics and a user agent together. Verify the profile exists in your Puppeteer version.
Control what part of the page is saved page.screenshot() options Choose viewport, full page, or a clipped region independently of DPR.
Configure reported screen information in headless mode Screen configuration APIs This is a separate concern from ordinary page viewport emulation.

Do not confuse the viewport’s deviceScaleFactor with the separate ScreenInfo.devicePixelRatio field in Puppeteer’s screen configuration API. For the page-emulation workflow in this guide, set deviceScaleFactor on the viewport. See Puppeteer’s screen configuration guide.

5. Wait for the state you need

A page being navigated does not guarantee that every image, font, animation, or client-rendered component is ready for capture. The example uses networkidle2, but no single navigation wait condition fits every site. If the page has a reliable readiness marker, wait for it explicitly; otherwise, a short delay can be used for a known late-rendering element.

await page.goto('https://example.com', {waitUntil: 'domcontentloaded'});
await page.waitForSelector('main article', {visible: true});
await page.screenshot({path: 'ready.png'});

Use a selector that reflects the content you need, not merely a generic element that appears before the page is usable. For animations or continuously active network requests, a network-idle wait may be slow or never settle; wait for a meaningful page condition instead.

6. Troubleshooting

Symptom Likely cause Fix
Screenshot looks like desktop layout The page was loaded before mobile viewport settings were applied, or only DPR was changed. Set viewport dimensions and any needed mobile or touch flags before goto(). Use a device profile if you also need its user agent.
Changing the viewport reloads or disrupts the page Viewport changes resize the page; changes to mobile or touch properties can reload it. Configure emulation before navigation, then load the page once under the intended conditions.
Screenshot dimensions are unexpected CSS viewport size, DPR, and capture region are being treated as one setting. Check viewport dimensions and deviceScaleFactor separately from fullPage, clip, or captureBeyondViewport. Exact output dimensions can depend on capture mode and browser implementation.
A named device profile is undefined The chosen name is not present in the installed Puppeteer version, or the import is incorrect. Inspect the installed version’s KnownDevices API and use an available name with the documented import.
Page content is missing or half-rendered Capture ran before the site finished rendering its content, or a generic network-idle condition did not match the site. Wait for a specific visible selector or other page readiness condition before capture.
PNG output ignores a quality setting JPEG quality does not apply to PNG. Use a format with quality support, such as JPEG, if lossy compression is acceptable; otherwise keep PNG.

7. Performance, reliability, and cost

  • Pixel count: Increasing DPR can increase the amount of raster output and memory needed to produce it. Very large full-page captures are especially demanding; use a viewport or clip if a full document image is unnecessary.
  • Readiness: Pick a wait condition suited to the site. Waiting for all network activity can add latency on pages with analytics, streaming, or long-lived requests.
  • Repeatability: Keep viewport metrics, profile, browser version, user agent, and readiness condition consistent when comparing captures. A device profile includes a user agent; a manual viewport may need one set separately if the site’s behavior depends on it.
  • Failure handling: Put browser closure in a finally block as in the examples so the browser process is closed even when navigation or capture fails. Set an appropriate navigation timeout for your workload and handle navigation errors at the job level.
  • Cost: Puppeteer is a browser automation library; the code itself does not introduce a per-screenshot API charge. Your compute, browser hosting, storage, and engineering costs depend on where and how you run it. DPR and image format choices affect output size and processing demands.

Or skip the browser setup

If you need a screenshot without managing Puppeteer and a browser, ScreenshotNeo returns an image or PDF from one GET request. Its API also accepts commonly used screenshot API parameter names, which can make switching easier. See the ScreenshotNeo API documentation for request options.

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 Bun.write('shot.webp', res);

ScreenshotNeo accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.

Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card.

FAQ

Does a higher DPR make the page layout wider?

No. DPR sets emulated pixel density; the viewport width and height determine the CSS layout area.

Should I use deviceScaleFactor or ScreenInfo.devicePixelRatio?

For ordinary page viewport emulation, set deviceScaleFactor in page.setViewport() or use a known device profile. ScreenInfo belongs to Puppeteer’s separate screen configuration API.

Will a known device profile guarantee identical results to a physical phone?

No. It applies Puppeteer’s bundled emulation metrics and user agent. The reviewed documentation does not establish that this reproduces every physical device or site behavior.

Can I set the viewport after navigation?

You can, but resizing can affect the page and changing mobile or touch properties can reload it. Set the intended emulation before navigation for more predictable captures.