ScreenshotNeo

BlogHow-to

How to Set a Fixed Viewport and Device Scale Factor for Playwright Screenshot Tests

Set Playwright’s viewport and device scale factor explicitly for repeatable screenshot tests. Learn how screenshot scale, projects, and dynamic content affect visual baselines.

By the ScreenshotNeo team4 October 20266 min read

Set viewport and deviceScaleFactor explicitly before navigating to make Playwright screenshot tests use a predictable browser size and pixel density. For Playwright Test, configure them under use; for the library API, pass them to browser.newContext(). Choose screenshot output scale separately: it determines whether the image has one pixel per CSS pixel or per device pixel.

1. Configure a fixed viewport in Playwright Test

Set the context options in playwright.config.ts. This applies to pages created by the test runner using that configuration.

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

export default defineConfig({
  use: {
    viewport: { width: 1280, height: 720 },
    deviceScaleFactor: 1,
  },
});

Keep the dimensions and scale factor alongside the screenshot baseline. If either changes, the rendered image can change too, so a comparison against an older baseline may no longer represent the same setup.

Playwright documents defaults of 1280 × 720 for the viewport and 1 for deviceScaleFactor. Stating the values explicitly makes the intended environment clear and guards against accidental configuration drift. See the official Playwright TestOptions reference.

2. Configure a browser context directly

If you are using Playwright without the test runner, create a context with the same explicit settings. The context-level settings apply to pages created in that context.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  viewport: { width: 1280, height: 720 },
  deviceScaleFactor: 1,
});
const page = await context.newPage();

await page.goto('https://example.com');
await page.screenshot({ path: 'example.png', scale: 'css' });

await browser.close();

Install the package with npm install playwright and install its browser binaries with npx playwright install chromium if needed. The context options are documented in the BrowserType API.

3. Understand viewport size, device scale, and screenshot size

  • Viewport is the page’s CSS layout area, expressed as width and height in CSS pixels. It affects responsive breakpoints and layout.
  • Device scale factor emulates the ratio between device pixels and CSS pixels. Set it explicitly so the browser context has the same pixel density from run to run.
  • Screenshot scale controls the output image dimensions. With scale: 'css', the screenshot has one image pixel per CSS pixel. With scale: 'device', it has one image pixel per device pixel; at a higher device scale factor, the resulting image can be larger.
// One output pixel per CSS pixel
await page.screenshot({ path: 'baseline-css.png', scale: 'css' });

// One output pixel per device pixel
await page.screenshot({ path: 'baseline-device.png', scale: 'device' });

Use the same screenshot scale for baseline creation and comparison. A viewport of 1280 × 720 with scale: 'css' produces a 1280 × 720 output image. Device scale affects device-pixel output; it does not change the CSS viewport dimensions. See Playwright’s Page screenshot API.

4. Set the viewport before navigation

Prefer setting the viewport in the test configuration or context before loading the page. Some sites respond to size changes with their own layout behavior, so resizing after load may not be equivalent to starting at the target size.

For an intentional one-page resize, use page.setViewportSize():

await page.setViewportSize({ width: 1280, height: 720 });
await page.goto('https://example.com');

The viewport change also resets screen. If both the page viewport and emulated screen dimensions need precise control, set viewport and screen when creating the context rather than relying on a later resize. The behavior is described in the Page API.

5. Use projects for separate browsers or viewport baselines

When you need more than one browser engine or environment, define Playwright Test projects. Treat browser engine, viewport dimensions, device scale factor, and screenshot output scale as separate comparison axes. Give each materially different configuration its own baseline.

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

export default defineConfig({
  projects: [
    {
      name: 'chromium-desktop',
      use: {
        browserName: 'chromium',
        viewport: { width: 1280, height: 720 },
        deviceScaleFactor: 1,
      },
    },
    {
      name: 'webkit-retina',
      use: {
        browserName: 'webkit',
        viewport: { width: 1280, height: 720 },
        deviceScaleFactor: 2,
      },
    },
  ],
});

Projects can also be used for emulated devices. Keep the intended project and screenshot scale identifiable when saving or reviewing baselines. See Playwright projects and Playwright emulation.

6. Stabilize other changing visual inputs

A fixed viewport and scale factor control two important inputs, but they do not freeze the page’s content. Animations, timestamps, rotating content, asynchronous data, and remote assets can still make screenshots differ. Make the page state consistent before capture. For unavoidable dynamic regions, Playwright’s screenshot API supports masking locators; a screenshot can also apply a stylesheet to adjust the page for capture.

await page.screenshot({
  path: 'stable.png',
  scale: 'css',
  animations: 'disabled',
  mask: [page.locator('[data-testid="live-clock"]')],
  style: '[data-testid="live-clock"] { visibility: hidden !important; }',
});

Use a mask when the region should remain in the image but be covered for comparison. Use a stylesheet when you need to alter page rendering for the capture. These options are documented in the screenshot API.

7. Troubleshooting fixed-size screenshots

Symptom Likely cause Fix
Screenshot dimensions vary between runs The viewport is unset, set to null, or depends on a host window. Set explicit width and height in the test configuration or context. Avoid viewport: null when you need a deterministic environment; it depends on the operating-system window size.
Image looks blurry or has unexpected dimensions The device scale factor or screenshot scale differs from the baseline setup. Set deviceScaleFactor explicitly and use the same scale: 'css' or scale: 'device' for baseline creation and comparison.
Responsive layout does not match the expected breakpoint The page was resized after navigation, or the configured viewport dimensions are not the intended CSS dimensions. Configure the viewport before navigation and check the width and height in CSS pixels.
Emulated screen dimensions are unexpected page.setViewportSize() resets screen. Set both screen and viewport on the context if both must be controlled.
Images differ despite fixed dimensions Dynamic page content or a different browser engine is affecting rendering. Stabilize content and mask or style dynamic regions. Keep separate baselines for different browser projects.
High-DPI screenshot file is much larger than expected scale: 'device' outputs one pixel per device pixel. Use scale: 'css' if the baseline should contain one output pixel per CSS pixel.

8. Or skip the browser setup

If you need a screenshot of a live URL rather than a local Playwright test baseline, ScreenshotNeo offers a website screenshot API. It can return PNG, JPEG, WebP, or PDF, and its API parameters include viewport and device-preset options. 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}`);

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Visit ScreenshotNeo and sign up for 1,000 free screenshots a month, with no card required.

9. Performance, reliability, and cost notes

Explicit context settings add no external service dependency: they define the browser environment used by the test. For reliable comparisons, keep the same browser engine, viewport, device scale factor, screenshot scale, and relevant page state across runs. If you increase device scale and capture at device scale, output images can become larger, which can increase storage and image-comparison work.

Local Playwright screenshot tests incur the compute and storage costs of the environment where they run. The ScreenshotNeo API is a separate option for URL-based captures; its published plans range from 1,000 free shots monthly to paid plans beginning with 3,000 for $5. Its billing rules mean the listed bot checks, blank pages, timeouts, failed loads, and cache hits are not billed.

Frequently asked questions

Does changing the device scale factor change CSS media query widths?

The viewport width is the CSS layout dimension. Device scale factor controls the emulated ratio of device pixels to CSS pixels. Set both explicitly when repeatability matters.

Should I use scale: 'css' or scale: 'device'?

Use CSS scale for one output pixel per CSS pixel. Use device scale when the image should represent device pixels, accepting that the output can be larger at higher density.

Can I use one baseline for Chromium and WebKit?

Keep baselines tied to their browser project. Browser engine is an independent rendering variable even when viewport and scale match.

What if a test needs a different viewport for one page?

Use page.setViewportSize() for an intentional page-level change. If screen dimensions also matter, configure them with the context.