Use Puppeteer’s device emulation before you navigate. Call page.emulate() with a descriptor from puppeteer.KnownDevices, or set the viewport and user agent yourself, then capture with page.screenshot(). This reproduces browser-facing viewport, scale, touch, and user-agent conditions; it is not a guarantee of every physical-phone behavior.
What Puppeteer mobile emulation changes
Puppeteer emulation configures the browser page, not a real handset. A known device descriptor combines a user agent with device metrics. The shortcut is equivalent to setting the user agent and viewport separately, according to the Page API.
Viewport width and height are CSS pixels. Device scale factor controls the relationship between CSS pixels and rendered device pixels. isMobile controls whether the page’s meta viewport tag is honored, and hasTouch enables touch support. These are independent settings documented in the Viewport interface.
Emulation should happen before page.goto(). Many sites do not expect a phone-sized resize after navigation, and changing mobile or touch settings can reload a page. Check the descriptor name against the Puppeteer version installed in your project; the available KnownDevices collection is version-sensitive.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →#1 Best Overall
Prerequisites and project setup
- Node.js with a project directory.
- Puppeteer installed with
npm install puppeteer. - A device name that exists in your installed release’s
puppeteer.KnownDevices. - A URL you are authorized to capture.
The examples use ECMAScript modules. Add "type": "module" to package.json, or adapt the import to your project’s module system.
Capture a known mobile device
This complete script emulates an iPhone descriptor, waits for the network to become reasonably idle, and writes a full-page PNG.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
const device = puppeteer.KnownDevices['iPhone 13'];
if (!device) throw new Error('Device descriptor is not available in this Puppeteer version');
await page.emulate(device);
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'mobile.png', fullPage: true });
} finally {
await browser.close();
}
The official screenshot guide shows the same general sequence—navigate with networkidle2, then call page.screenshot()—but network idleness is not proof that animations, lazy images, or application data have finished. Add an application-specific wait when those states matter.
List available device descriptors
Do not assume a descriptor exists across releases. Print the names from the package you actually installed:
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →import puppeteer from 'puppeteer';
console.log(Object.keys(puppeteer.KnownDevices).sort());
Use one of the printed keys exactly. If a name is missing after an upgrade, select another descriptor or configure the metrics manually.
Rank #2
Configure a phone without a preset
Separate settings are useful when you need a nonstandard viewport or want to make each behavior explicit.
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.setUserAgent(
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1'
);
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'custom-mobile.png' });
} finally {
await browser.close();
}
Choosing viewport values
| Goal | Setting | Effect |
|---|---|---|
| Trigger responsive layout | width, height |
CSS-pixel viewport dimensions |
| Represent a dense display | deviceScaleFactor |
Device scale factor; default is 1 |
| Honor mobile meta viewport | isMobile: true |
Includes the page’s meta viewport behavior; default is false |
| Exercise touch interactions | hasTouch: true |
Reports touch support; default is false |
A high device scale factor changes rendered pixel density, not the CSS width used by responsive breakpoints. Set width and height for layout testing, then choose scale for the output density you need.
Choose the screenshot you actually need
Viewport or full document
The default screenshot captures the visible viewport. Use fullPage: true to request the entire document:
Free tools Windows power users keep installed
One-click scans. No signup required.
await page.screenshot({
path: 'long-page.webp',
type: 'webp',
quality: 85,
fullPage: true
});
PNG is the default format. JPEG and WebP can reduce file size; quality ranges from 0 to 100 and does not apply to PNG. Confirm the options for your installed release in the ScreenshotOptions reference.
Capture a region
Use clip for a rectangle rather than the document:
await page.screenshot({
path: 'hero.png',
clip: { x: 0, y: 0, width: 390, height: 300 },
captureBeyondViewport: true
});
captureBeyondViewport controls whether the clipped area may lie outside the current viewport. A clip and fullPage solve different problems: one selects coordinates, the other requests the whole document.
Transparent output
Hide the default page background with omitBackground: true:
await page.screenshot({ path: 'transparent.png', omitBackground: true });
The page must itself allow transparency; an opaque CSS background will still be rendered.
Capture one element
For a component rather than the page, locate it and call ElementHandle.screenshot(). Puppeteer attempts to scroll a hidden element into view first.
const card = await page.$('[data-testid="product-card"]');
if (!card) throw new Error('Product card not found');
await card.screenshot({ path: 'product-card.png' });
Wait for a stable mobile render
Navigation completion and visual completion are different. Combine a navigation policy with waits that match your application:
- Navigate after emulation, using
waitUntil: 'networkidle2'when ongoing background requests are not expected. - Wait for a meaningful selector, such as the mobile navigation or main content.
- Wait for images or fonts if your page reveals them after JavaScript runs.
- Disable or finish animations where deterministic pixels matter.
- Capture only after lazy-loaded content has been brought into the document’s rendered area.
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForSelector('main');
await page.evaluate(() => document.fonts?.ready);
await new Promise(resolve => setTimeout(resolve, 500));
await page.screenshot({ path: 'stable-mobile.png', fullPage: true });
A fixed delay is a fallback, not a universal guarantee. Prefer a selector or application event that represents the state you intend to document.
Rank #4
Make captures repeatable
- Use the same Puppeteer version, Chromium revision, viewport, scale factor, user agent, and locale for comparisons.
- Emulate before every navigation, including after opening a new page.
- Use a deterministic URL and test data; personalized or time-dependent pages will legitimately differ.
- Record the descriptor name and custom settings alongside each image.
- Choose full-page capture only when the document length is part of the test; viewport captures are faster and easier to compare.
- Keep screenshots in a format appropriate to the job: PNG for pixel fidelity, JPEG or WebP for smaller photographic output.
Emulation covers browser-visible configuration. It should not be treated as proof that a physical phone’s GPU, battery, sensors, camera, OS text rasterization, or every browser quirk behaves identically.
Troubleshooting common failures
“Cannot read properties of undefined” for a device
Cause: the descriptor name is not present in this Puppeteer release. Fix: print Object.keys(puppeteer.KnownDevices), use an exact available key, or switch to setViewport() and setUserAgent().
The site still shows a desktop layout
Cause: emulation was applied after navigation, the viewport is too wide, or the page’s responsive rules depend on a user agent or meta viewport. Fix: create a new page, emulate before goto(), verify CSS-pixel width, and set isMobile: true when configuring manually.
Touch handlers do not run
Cause: hasTouch is false. Fix: use a descriptor that supplies touch or set hasTouch: true. This reports touch capability; it does not reproduce every hardware gesture.
Images are missing in a full-page shot
Cause: lazy loading has not been triggered or the capture started before the application finished. Fix: scroll through the page, wait for the image selectors or load events, then capture.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteBest Value
- Used Book in Good Condition
The screenshot is unexpectedly huge
Cause: fullPage: true includes the complete document, and a large device scale factor increases output pixels. Fix: use a viewport capture or clip, lower the scale factor, or resize the resulting image for delivery.
Changing settings reloads the page
Cause: Puppeteer may reload when mobile or touch metrics change. Fix: set all metrics before navigation and wait for the page to settle after any unavoidable change.
Network idle never arrives
Cause: analytics, WebSockets, polling, or advertisements keep connections open. Fix: use domcontentloaded plus explicit selectors and application readiness checks instead of waiting indefinitely for network idle.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF, while the service handles browser setup remotely. For a mobile-style capture, pass the viewport and device-related options supported by its API; the parameter names used by other screenshot APIs also work, which can simplify migration. See the ScreenshotNeo documentation for the current option names.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
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)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
It removes cookie-consent banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and whether the request was billed. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.
FAQ
Does Puppeteer emulate a real iPhone?
It emulates documented browser metrics and user-agent behavior. It does not establish complete physical-device fidelity.
Should I use page.emulate() or manual settings?
Use a known descriptor for a standard device profile. Use manual viewport and user-agent settings for custom dimensions or explicit control.
What is the difference between fullPage and clip?
fullPage requests the entire document; clip selects a rectangular region.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

