How to Take a Mobile Viewport Screenshot with Playwright
Emulate a mobile device or set a custom viewport, then capture the visible page with Playwright. Includes runnable examples, options, and fixes.
To take a mobile viewport screenshot with Playwright, configure a mobile device profile or a custom viewport before navigating, then call page.screenshot(). By default, Playwright captures the currently visible viewport; set fullPage: true only when you want the full scrollable page.
Use a device profile in Playwright Test
A device profile configures more than width and height: it can include viewport and screen dimensions, user agent, and touch behavior. This TypeScript example uses the documented iPhone 13 preset as an example profile. It represents browser emulation, not a guarantee that the result matches every iPhone or iOS version.
- Install Playwright Test if it is not already in your project:
npm install -D @playwright/test. - Save the project configuration as
playwright.config.ts. - Save the test below as
tests/mobile-screenshot.spec.ts, then runnpx playwright test.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{
name: 'Mobile Safari',
use: {
...devices['iPhone 13'],
},
},
],
});
import { test } from '@playwright/test';
test('capture mobile viewport', async ({ page }) => {
await page.goto('https://example.com');
await page.screenshot({ path: 'mobile-viewport.png' });
});
The screenshot is saved to the test working directory. Change the path to a directory your process can write to if you want to collect the image elsewhere.
Run a standalone JavaScript script
Use the standalone package when you want a script instead of a Playwright Test suite. Install it with npm install playwright. The script launches Chromium, creates a context from a device descriptor, visits the target, saves the visible viewport, and closes the browser.
const { chromium, devices } = require('playwright');
(async () => {
const browser = await chromium.launch();
try {
const context = await browser.newContext({
...devices['iPhone 13'],
});
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'mobile-viewport.png' });
await context.close();
} finally {
await browser.close();
}
})();
For an ES module project, import chromium and devices from playwright and use the same async flow. The try/finally ensures the browser closes even if navigation or capture fails.
Choose a device profile or a custom viewport
Choose a named device profile when the page behavior depends on mobile-like browser settings in addition to dimensions, such as touch support or the user agent. Choose a custom viewport when width and height are the requirements and you do not need the other emulated parameters.
| Approach | Use it when | What to account for |
|---|---|---|
| Device profile | You want a known emulated device configuration. | The profile supplies multiple settings; it is still browser emulation, not a physical-device capture. |
| Custom viewport | You need a particular width and height. | A viewport alone does not imply the same user agent, touch behavior, or screen settings as a device profile. |
Set a custom viewport at context creation:
const context = await browser.newContext({
viewport: { width: 390, height: 844 },
});
Or change it on a page after creating the context:
await page.setViewportSize({ width: 390, height: 844 });
When overriding a profile, spread the profile first and put your override afterward so the explicit viewport wins:
const context = await browser.newContext({
...devices['iPhone 13'],
viewport: { width: 390, height: 844 },
});
Describe a custom-dimension result as a mobile-sized or emulated screenshot. A width and height pair does not establish that the page was captured on a real phone.
Viewport capture versus full-page capture
Without extra options, page.screenshot() captures the current visible viewport. To capture the full scrollable document, opt in explicitly:
await page.screenshot({ path: 'mobile-viewport.png' });
await page.screenshot({ path: 'mobile-full-page.png', fullPage: true });
A full-page image can be much taller than the emulated screen. Use viewport capture for a screen-sized artifact, such as checking the first screen of a responsive layout. Use fullPage: true when the entire document is the intended artifact. See the [Playwright Page API](https://playwright.dev/docs/api/class-page#page-screenshot) for the fullPage option.
Control screenshot scale
The scale screenshot option controls output pixels:
scale: 'css'creates one image pixel per CSS pixel.scale: 'device'creates one image pixel per device pixel, so high-DPI settings can produce a larger image.
await page.screenshot({
path: 'mobile-css-scale.png',
scale: 'css',
});
Choose the scale to match the consumer of the image. For visual comparisons, keep it consistent between baseline and current captures. For artifacts sent over a network or stored in bulk, remember that more output pixels can mean larger files.
Make the capture repeatable
Set the emulation configuration before navigation so the page loads under the intended viewport and device parameters. If the page needs time to render content, wait for an application-specific readiness condition before capturing. For example, wait for a page element your application renders when it is ready:
await page.goto('https://example.com');
await page.getByRole('heading', { name: 'Example Domain' }).waitFor();
await page.screenshot({ path: 'mobile-viewport.png' });
Use a condition tied to the page’s actual content rather than relying on an arbitrary delay where possible. Dynamic content, animations, fonts, and changing data can make images differ across runs even at the same viewport.
For visual regression assertions, Playwright Test provides toHaveScreenshot(). Keep baseline and comparison runs in a consistent environment: rendering can vary with operating system, browser version, settings, hardware, power source, and headless mode. Consult the [Playwright visual comparisons guide](https://playwright.dev/docs/test-snapshots) when setting up screenshot assertions.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| The image has desktop dimensions. | The page or context was created without the device profile or intended viewport, or the viewport was overridden later. | Set the profile or viewport on the context before creating the page. If combining settings, put the custom viewport after the profile spread. |
| The screenshot includes only the first screen. | Viewport-only capture is the default. | Set fullPage: true when the full scrollable document is desired. |
| The screenshot is larger than expected. | scale: 'device' can output device pixels, especially for high-DPI emulation. |
Use scale: 'css' for one output pixel per CSS pixel, and check the effective viewport and scale. |
| The page looks different from a physical phone. | Playwright emulates browser parameters; a preset is not a capture from every physical device or OS release. | Use a profile for browser emulation, and validate on physical hardware separately when real-device behavior is part of the requirement. |
| The screenshot is blank or misses late content. | The capture may happen before the page or application content is ready. | Wait for a meaningful selector or application readiness condition before calling screenshot(). |
| Visual comparisons fail intermittently. | Rendering can vary across environments or because the page content changes. | Keep the browser and execution environment consistent, and stabilize dynamic page content before comparing. |
| The script fails to save the image. | The destination directory may not exist or may not be writable by the process. | Use an existing writable path or create the destination directory before capture. |
Performance, reliability, and cost
Each local capture requires launching or reusing a browser, navigating to the page, waiting for the necessary content, and writing an image. Reuse a browser process for a sequence of captures rather than launching one for every URL when your workload permits, and close contexts and browsers when finished. Full-page and device-scale images can produce more pixels and larger artifacts than viewport-only CSS-scale images.
For reliable results, keep the browser version, operating system, viewport, device parameters, page data, and readiness condition stable when comparing screenshots. Treat timeouts, navigation failures, and pages that require authentication as workflow concerns: handle them explicitly and avoid treating an incomplete page as a valid baseline.
Playwright is an open-source automation framework; the code above does not specify a hosted browser service or a per-screenshot price. Your costs depend on where the browser runs and the compute, storage, and network resources your workflow uses.
Or skip the browser setup
ScreenshotNeo can return a screenshot through one API request. 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}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', image));
ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step 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 to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month, with no card.
FAQ
Does Playwright capture the browser chrome?
page.screenshot() captures page content, not the surrounding browser interface.
Does a mobile preset guarantee the same result on every iPhone?
No. It configures browser emulation parameters. Browser version, operating system, hardware, and page behavior can still affect rendering.
Should I use a device preset for responsive layout checks?
Use one when the test needs the preset’s device-like parameters. If the requirement is only a particular content width and height, a custom viewport is sufficient.
Can I save a mobile screenshot as a PDF?
This guide captures PNG screenshots with Playwright’s page screenshot API. PDF generation is a separate browser operation; use a PDF workflow when a document rather than a raster image is required.


