ScreenshotNeo

BlogHow-to

How to Capture a Responsive Website Screenshot at iPhone 16 Size with Playwright

Use Playwright’s iPhone 16 emulation to capture a responsive page, choose viewport or full-page output, and understand CSS versus device-pixel dimensions.

By the ScreenshotNeo team4 October 20268 min read

Use Playwright’s built-in iPhone 16 device descriptor to emulate a mobile browser, open the page, and call page.screenshot(). By default, it captures the visible viewport. Set fullPage: true to capture the full scrollable document. This is a browser emulation profile, not a screenshot from physical iPhone 16 hardware. Playwright’s emulation guide explains how device profiles configure browser behavior.

1. What “iPhone 16 size” means in Playwright

The current Playwright device registry lists the iPhone 16 profile with a 393 × 659 CSS-pixel viewport, a 393 × 852 CSS-pixel screen, and a device scale factor of 3. It also enables mobile and touch behavior and identifies WebKit as the default browser engine. These are emulation settings for browser layout and input; they do not reproduce every behavior of a physical phone.

Apple lists the iPhone 16 display resolution as 2556 × 1179 pixels at 460 ppi. That hardware-panel specification is different from the emulated CSS viewport. With Playwright’s default device-pixel screenshot scale, a viewport screenshot is nominally 1179 × 1977 image pixels: 393 × 3 by 659 × 3. With CSS scale, it is 393 × 659 pixels. The full-page image height depends on the document. See the Playwright device registry and Apple iPhone 16 specifications.

Setting or output Meaning
Viewport: 393 × 659 CSS px The page area used for responsive layout and the default visible capture region.
Screen: 393 × 852 CSS px The emulated screen dimensions in the device profile; not the same as the page viewport.
Device scale factor: 3 Default screenshot output uses three image pixels per CSS pixel.
Default screenshot Visible viewport, device-pixel scale, PNG format unless another type is requested.
scale: 'css' One output image pixel per CSS pixel.
fullPage: true The full scrollable document, whose resulting height depends on content.

2. Set up a Playwright Test project

Install Playwright Test and its browser binaries if they are not already available in your project:

npm init playwright@latest

Choose TypeScript when prompted, or add the test dependency and install the browsers in an existing project. The following configuration creates a project using the iPhone 16 descriptor and explicitly selects WebKit, the engine named as the preset’s default.

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

export default defineConfig({
  projects: [
    {
      name: 'iPhone 16 emulation',
      use: {
        ...devices['iPhone 16'],
        browserName: 'webkit',
      },
    },
  ],
});

Create a test that navigates to your page and writes the screenshot. Ensure the artifacts directory exists first; Playwright does not create a missing parent directory for the screenshot path.

// tests/iphone16-screenshot.spec.ts
import { test } from '@playwright/test';

 test('capture the responsive page at iPhone 16 size', async ({ page }) => {
  await page.goto('https://example.com');
  await page.screenshot({ path: 'artifacts/iphone-16.png' });
});

Run the test with:

npx playwright test tests/iphone16-screenshot.spec.ts --project='iPhone 16 emulation'

The project’s name is only a label. The browserName setting selects the engine. A device profile used with Chromium still gives you an iPhone-sized emulation in Chromium; it does not turn Chromium into iOS Safari.

3. Choose viewport or full-page capture

The default screenshot is the visible viewport. It is appropriate for reviewing the first screen or a particular scroll position. To capture the entire scrollable page, pass fullPage: true:

await page.screenshot({
  path: 'artifacts/iphone-16-full.png',
  fullPage: true,
});

A full-page image can be very tall. It represents the document’s scrollable content, not a page that physically fits on the phone display. For a capture starting at a particular position, scroll before calling screenshot(); without fullPage, the screenshot shows the current viewport.

4. Choose image scale and format

By default, Playwright uses device-pixel scale. At the iPhone 16 profile’s factor of 3, each CSS pixel contributes three output pixels in each dimension. To keep the image at CSS-pixel dimensions, set scale: 'css':

await page.screenshot({
  path: 'artifacts/iphone-16-css.png',
  scale: 'css',
});

You can combine CSS scale with a full-page capture:

await page.screenshot({
  path: 'artifacts/iphone-16-full-css.png',
  fullPage: true,
  scale: 'css',
});

PNG is the screenshot API’s default and is a practical choice for crisp layout inspection. Playwright also supports JPEG and, where supported by the API and browser, quality settings. Refer to the Page screenshot API for the current option list and details.

5. Use the standalone Playwright library

For a script rather than a Playwright Test suite, import the device registry and create a context with the preset. This runnable example uses WebKit so the selected engine matches the descriptor’s default:

// screenshot.mjs
import { webkit, devices } from 'playwright';

const browser = await webkit.launch();
try {
  const context = await browser.newContext({ ...devices['iPhone 16'] });
  const page = await context.newPage();
  await page.goto('https://example.com', { waitUntil: 'load' });
  await page.screenshot({ path: 'iphone-16.png' });
  await context.close();
} finally {
  await browser.close();
}

Install the standalone package and WebKit browser if needed:

npm install playwright
npx playwright install webkit

You can launch Chromium or Firefox and still apply the iPhone profile, but the result then reflects that engine’s rendering and behavior. State the engine when sharing screenshots or comparing them across runs.

6. Wait for the page to be ready

A screenshot taken immediately after navigation can miss content loaded by client-side code, images, fonts, or asynchronous requests. Choose a wait condition that matches the page instead of adding an arbitrary long delay. For example, wait for a page-specific element:

await page.goto('https://example.com');
await page.locator('main').waitFor({ state: 'visible' });
await page.screenshot({ path: 'artifacts/iphone-16.png' });

For a site with a clear loading indicator, wait for that indicator to disappear. For a known animation, disable or wait for the animation as part of your capture flow. Network-idle can be unsuitable for pages that maintain long-lived connections or make continuous background requests. If you use a fixed timeout, keep it as short as the page’s known behavior permits and understand that it may still be too short or unnecessarily long.

7. Troubleshooting

Symptom Likely cause Fix
“Cannot find device” or undefined device settings The installed Playwright version does not include the named descriptor, or the device name is misspelled. Update Playwright and verify the exact registry key is iPhone 16. Keep the package version consistent between configuration and browser installation.
Browser executable is missing The package is installed but its browser binary is not. Install the selected engine’s browser, such as npx playwright install webkit.
The image has unexpected dimensions You are comparing CSS pixels with device pixels, or captured the full page instead of the viewport. Check scale and fullPage. The default uses device scale; use scale: 'css' for one output pixel per CSS pixel.
The page looks different from iPhone Safari The run used Chromium or Firefox, or emulation differs from physical hardware. Use WebKit for the closest engine match and validate critical behavior on a physical iPhone when hardware-specific behavior matters.
Screenshot file is not written The output path’s parent directory does not exist or the process lacks write access. Create the directory before capture and use a writable path.
Screenshot is blank or missing late content Navigation finished before the app rendered its content, or a resource failed to load. Wait for a meaningful visible selector, inspect navigation and console errors, and verify that required resources can load.
Full-page screenshot is extremely tall The document is tall, contains an infinite-scroll feed, or expands content as it is scrolled. Use viewport capture for a screen-specific check, or constrain the page state before requesting a full-page image.
Layout differs between runs Dynamic content, time-dependent UI, fonts, animations, or remote assets changed. Stabilize test data and page state, wait for critical assets, and use consistent browser and device settings.

8. Reliability, performance, and cost considerations

  • Repeatability: Pin the Playwright version and browser binaries in the project’s setup, keep the same engine and device descriptor, and stabilize page data where visual comparisons matter.
  • Capture time: Browser startup and page load usually dominate a single capture. Reuse a browser process for batches, while creating isolated contexts where test state must not leak.
  • Resource use: Full-page images and device-pixel output can be substantially larger than viewport or CSS-scale images. Select the capture region and scale to match the deliverable.
  • Failure handling: Set a navigation timeout appropriate for your site, detect failed navigation or missing page-specific elements, and save diagnostics when a capture is part of a pipeline.
  • Cost: Playwright is an open-source browser automation library; your direct costs depend on where you run browsers and the compute, storage, and CI resources you consume. This method does not require a screenshot API subscription.

9. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its API can return an image or PDF from one GET request, and the request options include viewport and device presets. The following cURL, Python, and Node.js examples capture a target page; see the ScreenshotNeo API documentation for the supported parameters and response details.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.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);
  • Cookie banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets can be removed before capture; each step can be turned off.
  • Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses identify page verdict and billing status in headers.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.

Sign up free for 1,000 screenshots a month, with no card required.

10. Frequently asked questions

Does this capture a screenshot from an actual iPhone 16?

No. It applies Playwright’s iPhone 16 emulation profile in a browser. Use a physical device when the result must validate hardware-specific behavior.

Should I use WebKit or Chromium?

Use WebKit when you want the browser engine associated with the preset’s default. Use Chromium or Firefox when those are the engines you need to test; report which one produced the image.

Why is my screenshot not 2556 × 1179?

That is Apple’s hardware display resolution. Playwright captures the page viewport, whose emulated CSS dimensions and device scale determine the output image dimensions.

Can I capture the full page at CSS-pixel scale?

Yes. Combine fullPage: true and scale: 'css'. The output width follows the CSS viewport, and its height follows the page content.

Sources