ScreenshotNeo

BlogGuides

Best Playwright Settings for Accurate Mobile Website Screenshots

Configure Playwright’s device profile, viewport, mobile behavior, and screenshot scale for stable mobile captures and visual comparisons.

By the ScreenshotNeo team4 October 20269 min read

For accurate mobile screenshots with Playwright, start with a device profile that matches the browser conditions you want to emulate, keep its viewport (or a deliberate override) fixed, and choose screenshot scale and capture scope explicitly. Run visual comparisons in a stable browser and host environment: a matching viewport alone does not guarantee matching pixels across machines, and emulation is not a promise of identical behavior on every physical phone.

This guide uses Playwright Test with TypeScript. The same device and screenshot principles apply when you use Playwright directly. Official references: Emulation, test use options, Page API, and visual comparisons.

1. Choose the device profile and viewport

A Playwright device descriptor can set more than screen dimensions: it can include a user agent, screen size, viewport, and touch behavior. Start with the documented profile closest to the browser and device conditions under test. For example, the preset named iPhone 13 is a starting point; it does not represent every iPhone or every mobile browser.

Install Playwright Test and its browser binaries using the official installation guide. Then create playwright.config.ts:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  use: {
    ...devices['iPhone 13'],
    // The explicit viewport overrides the preset viewport.
    // Omit this line to retain the profile's viewport.
    viewport: { width: 390, height: 844 },
    // Keep screenshot output dimensions predictable.
    deviceScaleFactor: 1,
    isMobile: true,
    hasTouch: true,
    colorScheme: 'light',
    locale: 'en-US',
    timezoneId: 'UTC',
  },
});

Spread the descriptor before an explicit override so your chosen viewport wins. Pick CSS-pixel dimensions deliberately and keep them unchanged across runs when comparing captures. If the target scenario is the preset as documented, remove the override and preserve all of its defaults instead of selectively replacing values without a reason.

Viewport, screen, and device pixel ratio

The viewport is the page’s visible CSS-pixel area. The device descriptor can also provide screen dimensions and a device scale factor. These values represent different aspects of the emulated browser; changing the viewport changes responsive layout breakpoints, while the scale factor affects device-pixel rendering and device-scale screenshot output. Keep the values aligned with the scenario you intend to test rather than assuming that a phone’s marketed resolution is the CSS viewport.

2. Set mobile behavior deliberately

isMobile controls whether the page’s meta viewport tag is taken into account and enables mobile behavior in the emulated context. hasTouch describes whether the viewport supports touch events. They are related but distinct settings. Use the profile’s values when they match your target, and change them only to model a specific condition.

For example, a site that lacks a proper <meta name="viewport"> declaration can lay out unexpectedly under mobile emulation. That can be a real defect worth capturing, so do not add a viewport tag or change emulation settings just to make the screenshot look more familiar. Playwright’s Android API documentation notes that isMobile is not supported in Firefox; check the browser and platform context for the exact setup you run.

3. Choose screenshot scale and capture extent

Use a viewport screenshot when the artifact should show only what is visible in the current viewport. Use fullPage: true when it should include the scrollable page. The scale option controls output pixels: css produces one image pixel per CSS pixel, while device produces one image pixel per device pixel and can create larger images on high-DPI profiles.

import { test, expect } from '@playwright/test';

test('mobile page visual baseline', async ({ page }) => {
  await page.goto('https://example.com', { waitUntil: 'load' });

  // Viewport capture: compact and tied to the configured CSS viewport.
  await expect(page).toHaveScreenshot('mobile-viewport.png', {
    fullPage: false,
    scale: 'css',
    animations: 'disabled',
  });
});

test('full mobile page capture', async ({ page }) => {
  await page.goto('https://example.com', { waitUntil: 'load' });

  await page.screenshot({
    path: 'mobile-full-page.png',
    fullPage: true,
    scale: 'css',
    animations: 'disabled',
  });
});

Replace https://example.com with the page under test. For visual assertions, Playwright creates or compares against a baseline according to its snapshot workflow; review and commit baselines intentionally. For a one-off artifact, page.screenshot writes the image without a visual assertion.

When to use CSS scale or device scale

Goal Suggested scale Tradeoff
Compact visual regression files with a consistent CSS-pixel mapping css Output is not a device-density pixel image.
Inspecting output at the emulated device’s pixel density device High-DPI output can be larger and comparisons depend on the configured device scale factor.

Keep viewport dimensions and scale consistent between baseline creation and comparison. Full-page captures can be substantially taller than viewport captures; pages with lazy-loaded content may require scrolling or an application-specific readiness condition before capture.

4. Make the browser context match the page

Set context values explicitly when they affect what the page renders. Playwright supports options such as locale, timezone, color scheme, geolocation, and permissions. These emulate browser context; setting timezoneId does not change the test runner process’s timezone.

use: {
  ...devices['iPhone 13'],
  viewport: { width: 390, height: 844 },
  locale: 'en-US',
  timezoneId: 'UTC',
  colorScheme: 'light',
  geolocation: { latitude: 37.7749, longitude: -122.4194 },
  permissions: ['geolocation'],
}

Only configure geolocation and permissions when the scenario needs them. Also make application state reproducible: use a known account or fixture, control feature flags and test data, and avoid capturing transient notifications or personalized content unless those are part of the test.

5. Keep visual comparisons stable

Playwright warns that screenshots can vary with host operating system, browser version, settings, hardware, power source, and headless mode. Its guidance is: “For consistent screenshots, run tests in the same environment where the baseline screenshots were generated.” Use the same CI image or machine class, browser version, Playwright version, and launch mode for baseline generation and later comparisons.

  • Pin the Playwright dependency and install its matching browser binaries.
  • Generate and compare baselines in the same operating system and browser environment.
  • Use deterministic test data and wait for the page state that matters, not an arbitrary long sleep.
  • Disable animations for visual snapshots when motion is irrelevant; if motion is under test, keep it enabled and control timing.
  • Account for fonts, image loading, clocks, randomized content, and third-party widgets that can change between runs.
  • Review screenshot diffs before updating baselines so a real regression is not silently accepted.

There is no single device preset that guarantees pixel-identical results across real phones. Treat Playwright emulation as a repeatable browser configuration. For device-specific behavior that matters, validate on the actual target device or a device workflow appropriate to your requirements. Playwright documents Android automation as experimental and describes platform requirements and limitations.

6. Complete example with a stable visual test

This runnable Playwright Test example combines a mobile descriptor, explicit viewport, predictable context, page readiness, and a CSS-scale full-page assertion. Save it as tests/mobile.spec.ts alongside the configuration above.

import { test, expect } from '@playwright/test';

test('landing page mobile layout', async ({ page }) => {
  await page.goto('https://example.com', { waitUntil: 'load' });
  await page.locator('main').waitFor({ state: 'visible' });

  await expect(page).toHaveScreenshot('landing-mobile.png', {
    fullPage: true,
    scale: 'css',
    animations: 'disabled',
  });
});

Run it with npx playwright test tests/mobile.spec.ts. If the target page has no main element, replace that locator with a stable element on the page. For an application with asynchronous content, wait for a meaningful ready selector or response before capturing; waitUntil: 'load' does not guarantee every app-specific render or third-party request is complete.

7. Troubleshooting

Symptom Likely cause Fix
Screenshot has a desktop layout Mobile behavior or viewport was not configured as intended, or the page does not respond to the emulated conditions. Use a suitable device descriptor, inspect its viewport and mobile flags, and verify the page’s viewport metadata and responsive CSS.
Viewport differs from the configured value The descriptor was spread after the override, replacing it. Spread ...devices['iPhone 13'] first, then set the explicit viewport.
Touch interactions do not work The context does not advertise touch support or the test is relying on a mouse-only interaction. Use a profile with the intended hasTouch value and exercise the interaction with an appropriate touch-capable API.
Firefox ignores mobile emulation isMobile is not supported in Firefox in the documented Android API context. Use a supported browser/context for that mobile emulation scenario, or test Firefox behavior separately with its supported options.
Images or sections are missing in a full-page shot Lazy content has not loaded, or the app is still rendering. Wait for the relevant content or scroll through the page to trigger lazy loading before capture.
Visual diff changes across machines Host OS, browser version, hardware, settings, or headless mode differs. Generate and compare baselines in the same stable environment with pinned browser dependencies.
Screenshot looks blurry or is unexpectedly large The chosen scale does not match the desired output pixel density. Use css for one output pixel per CSS pixel; use device when device-pixel output is required, and check the device scale factor.
Screenshot captures a loading state Page load completed before app data, fonts, or client-side rendering settled. Wait for an application-specific selector or state. Avoid relying on a fixed delay unless the delay itself is the behavior under test.

8. Performance, reliability, and cost

Screenshot cost in a Playwright setup is primarily the compute time and storage or CI capacity your own environment uses; the cited Playwright documentation does not specify a per-screenshot service price. CSS-scale viewport images are usually smaller than device-scale or full-page artifacts, so choose the smallest scope and density that satisfies the review or test. Full-page capture and large images take more resources to create, transfer, and retain.

For reliability, keep browser versions aligned with the Playwright package, run captures in a stable environment, and wait on specific page readiness signals. Retries can help with genuinely transient infrastructure failures, but retries that hide deterministic layout or loading failures make visual tests less useful. Emulation is convenient for repeatable responsive checks; use device-specific validation when the browser, hardware, or operating-system behavior itself is under test.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. If you want a screenshot without installing and maintaining a browser runner, one GET request returns an image or PDF. The parameter names used by other screenshot APIs also work, which can make switching easier. See the ScreenshotNeo API documentation.

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', new Uint8Array(await res.arrayBuffer()));

Cookie banners are accepted and removed before the shot, along with known newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

FAQ

Which viewport should I use for an iPhone screenshot?

Start with the Playwright profile matching the browser conditions you are targeting, then preserve its viewport or deliberately override it with fixed CSS-pixel dimensions. One profile is not a stand-in for every phone.

Does deviceScaleFactor: 1 mean the page is not mobile?

No. It controls the emulated device pixel ratio. Mobile behavior, viewport, touch support, and pixel ratio are separate choices; configure each to match the scenario.

Will a Playwright mobile screenshot match a real phone exactly?

Not universally. Emulation is useful for controlled responsive checks, while rendering can vary by browser, operating system, hardware, and settings. Validate on the real target when device-specific behavior is important.

Should I use fullPage for visual regression?

Use it when the whole scrollable page is the subject of comparison. For above-the-fold layout, a viewport capture is smaller and isolates the visible composition.