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 →Use Playwright’s built-in device descriptor when you want a realistic named phone, then capture with fullPage: true. A descriptor configures more than width: it sets user agent, viewport and screen behavior, touch support, mobile meta-viewport handling, and device scale factor. For an unlisted breakpoint, spread the closest descriptor and override its values after the spread.
1. Choose a device profile
Playwright emulates browser behavior rather than operating a physical handset. That distinction matters when you interpret layout bugs or pixel differences: the result comes from a desktop browser engine running with mobile settings, not from hardware sensors, firmware, or a handset GPU.
Use a built-in descriptor for a named phone
The devices registry includes profiles such as iPhone 13 and Pixel 9 Pro. A descriptor is the most reliable baseline because its related values stay consistent. It supplies a user agent, screen size, viewport, touch capability, mobile behavior, and a device scale factor.
Use a custom profile for a breakpoint
If your design target is, for example, a 390 × 844 CSS-pixel breakpoint rather than a particular handset, start from a nearby profile and override the values you need. Put overrides after ...devices[...]; otherwise the descriptor’s viewport and related settings can replace your custom values.
#1 Best Overall
2. Capture a full-page iPhone screenshot
Install Playwright, create the context before navigation, and then take the screenshot:
import { chromium, devices } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext({
...devices['iPhone 13'],
});
const page = await context.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'iphone-13.png', fullPage: true });
await browser.close();
fullPage: true makes Playwright capture the complete scrollable document instead of only the initial viewport. It does not change the emulated phone; it changes the document length included in the image.
Wait for content that appears after navigation
Network idle is useful for pages that load data shortly after the initial response, but it is not a guarantee that every image or animation has finished. For deterministic captures, wait for a meaningful selector and, where appropriate, disable animations in a test-only stylesheet:
await page.goto('https://example.com');
await page.locator('[data-testid="article"]').waitFor();
await page.addStyleTag({
content: `*, *::before, *::after {
animation: none !important;
transition: none !important;
}`,
});
await page.screenshot({ path: 'article-mobile.png', fullPage: true });
3. Build a custom mobile configuration
This Playwright Test configuration targets a controlled breakpoint while retaining the other mobile characteristics from a desktop browser descriptor:
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [{
name: 'custom-mobile',
use: {
...devices['Desktop Chrome'],
viewport: { width: 390, height: 844 },
isMobile: true,
hasTouch: true,
userAgent: 'custom mobile user agent',
deviceScaleFactor: 3,
},
}],
});
In this example, isMobile tells the browser to apply mobile meta-viewport behavior and enables touch events. hasTouch advertises touch support. The custom user agent can activate server-side mobile variants, while deviceScaleFactor controls the emulated pixel density.
Override order is significant
The object spread is evaluated from left to right. A value written before ...devices['iPhone 13'] can be overwritten by that descriptor. Write every intentional override after the spread and keep the remaining descriptor values unchanged unless your test has a specific reason to alter them.
4. Control pixel density with scale
There are two related settings:
deviceScaleFactor: the emulated device’s density, commonly 2 or 3 in mobile presets.- Screenshot
scale: the output policy.scale: 'device'produces one image pixel per device pixel, so a high-density profile creates a larger file. The default CSS scale produces a more compact, stable artifact.
// Compact CSS-pixel artifact
await page.screenshot({ path: 'mobile-css.png', fullPage: true });
// Device-pixel output for pixel-level rendering checks
await page.screenshot({
path: 'mobile-device-pixels.png',
fullPage: true,
scale: 'device',
});
Choose CSS scale for most visual-regression baselines when file size and consistent dimensions matter. Choose device scale when the purpose is to inspect or deliver the pixels a high-DPI display would use. Record the choice in your test configuration so later comparisons do not silently mix densities.
5. A reusable screenshot function
Keeping context creation and capture in one function makes it easier to run the same URL against several profiles:
Recommended Free Tools
import { chromium, devices } from 'playwright';
async function capture(name, url, descriptor) {
const browser = await chromium.launch();
try {
const context = await browser.newContext({ ...descriptor });
const page = await context.newPage();
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot({
path: `${name}.png`,
fullPage: true,
scale: 'css',
});
await context.close();
} finally {
await browser.close();
}
}
await capture('iphone-13-home', 'https://example.com', devices['iPhone 13']);
await capture('pixel-9-pro-home', 'https://example.com', devices['Pixel 9 Pro']);
Use the same browser engine and Playwright version for visual comparisons. Changing either can legitimately alter font rasterization, form controls, media-query behavior, or layout details.
6. Preset versus custom profile
| Decision | Built-in preset | Custom profile |
|---|---|---|
| Primary goal | Reproduce a named phone | Exercise an exact breakpoint or special server condition |
| Viewport | Registry value | Your explicit width and height |
| User agent | Descriptor value | Descriptor value or an intentional custom string |
| Touch and mobile behavior | Preset values | Set hasTouch and isMobile deliberately |
| Density | Preset deviceScaleFactor |
Your chosen factor |
| Interpretation | Closest registry simulation | Breakpoint-focused browser emulation, not a hardware replica |
7. Common failures and fixes
The page still looks like desktop
Check that the context was created with the descriptor before newPage() and before goto(). If you are using a custom profile, confirm that isMobile, viewport, and user agent are set after the spread. A page may also serve desktop markup based on cookies or server-side feature flags; clear the context or set the required state explicitly.
The width is not the value you configured
Inspect the final context options rather than assuming the override won. A later spread or project setting can replace the viewport. Keep one source of truth and place custom values after the descriptor.
Touch interactions do not work
Set hasTouch: true and use touch-capable page interactions where appropriate. isMobile also affects mobile meta-viewport handling. Neither setting proves that a physical touchscreen or mobile GPU is present.
The screenshot is unexpectedly huge
High deviceScaleFactor combined with scale: 'device' multiplies output pixels. Use the default CSS scale for a compact artifact, or lower the factor when density is not part of the test.
Lazy images are missing from a full-page shot
Full-page capture does not guarantee that an application’s lazy-loader has requested every asset. Scroll through the page or trigger the application’s loading condition before capture, then wait for the image selector or its network request.
The result changes between runs
Wait for a stable selector, freeze animations, use consistent test data, and keep browser and Playwright versions fixed. Ads, rotating content, current-time labels, and fonts loaded from different sources can all produce legitimate differences.
A page redirects or shows the wrong locale
Mobile user agents, cookies, timezone, and geolocation can affect redirects and content. Create a fresh context for each case and configure the locale-related state your application expects before navigation.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallBest Value
8. Performance, reliability, and artifact choices
- Context cost: launching one browser per screenshot is simple but slower. For a batch, launch once and create isolated contexts per device.
- Full-page cost: long documents require more layout, memory, and image encoding. Capture only the viewport when the question is responsive chrome; use full-page only when document completeness matters.
- Density trade-off: device-pixel output increases dimensions and storage. CSS-scale output is usually easier to diff and archive.
- Reproducibility: pin the Playwright version, browser binaries, viewport, descriptor, scale policy, fonts, and test data.
- Security: treat URLs, cookies, authorization headers, and captured files as sensitive. Do not print secrets in test logs or commit screenshots containing private data.
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server when you want a clean capture without maintaining Playwright infrastructure. Its request can return PNG, JPEG, WebP, or PDF; it accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Each step can be disabled.
Only clean shots are billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. The MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
For a one-call capture, see the ScreenshotNeo documentation:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in 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)
And in 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}`);
ScreenshotNeo includes full-page capture, device presets and custom viewports, retina scale, element selectors, dark mode, custom CSS and JavaScript, click and wait conditions, request or resource blocking, headers, cookies, user agents, timezone and geolocation, transparent backgrounds, resizing, chosen-TTL caching, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Its parameter names are compatible with those used by other screenshot APIs, which can simplify a migration.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; Growth is $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account to start with the 1,000-shot allowance.
Frequently Asked Questions
Does Playwright emulation equal testing on a real iPhone or Android phone?
No. It emulates browser settings and behavior—such as viewport, user agent, touch, meta-viewport handling, and density—inside a browser engine. Hardware-specific rendering and device firmware are not reproduced.
Should I use fullPage for responsive breakpoints?
Use it when you need the entire document in one image. For checking the layout visible on initial load, omit it so the screenshot represents only the configured viewport.
Why would two mobile presets with similar widths produce different layouts?
Their user agents, mobile flags, touch settings, scale factors, and other descriptor values can differ. Those settings influence both server responses and client-side media behavior.
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.

