ScreenshotNeo

BlogHow-to

Playwright Screenshot Uses the Wrong Page Size: Set Viewport and Device Scale Factor

Fix Playwright screenshots with the right viewport, device scale factor, and screenshot scale. Learn how to control output dimensions and troubleshoot mismatches.

By the ScreenshotNeo team4 October 20268 min read

A Playwright screenshot’s size depends on three separate settings: the viewport controls the page’s layout in CSS pixels, deviceScaleFactor controls device pixel ratio (DPR), and the screenshot’s scale controls how CSS or device pixels become image pixels. Set all three explicitly when output dimensions matter. Use fullPage only to capture beyond the visible viewport; it changes capture height, not pixel density.

This example requests a 1280 × 720 CSS-pixel viewport, DPR 1, and one output pixel per CSS pixel:

const { chromium } = require('playwright');

(async () => {
  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: 'page.png', scale: 'css' });

  await browser.close();
})();

Save this as screenshot.js, install Playwright with npm install playwright, then run node screenshot.js. The result is configured for a 1280 × 720 viewport and CSS-scale screenshot output. Page content, browser rendering, and device settings can affect what appears in the image, but the viewport and scale options make the requested dimensions explicit. See the official Page API and BrowserType API.

1. Understand which size is wrong

Before changing options, identify whether the mismatch is in the page layout, raster density, or captured area. These settings solve different problems:

What you mean by size Playwright setting What it changes
Width and height the page lays out into viewport Layout viewport dimensions in CSS pixels
Device pixel ratio deviceScaleFactor The emulated device’s pixel density
Image pixels per CSS pixel Screenshot scale Whether output pixels follow CSS pixels or device pixels
How much of the document is captured fullPage or clip Capture extent, such as viewport, full page, or a rectangle

Viewport dimensions are CSS pixels, not physical monitor pixels. For an image that should be exactly 1280 × 720 pixels, use a 1280 × 720 viewport and scale: 'css'. If the page is taller than the viewport, the default viewport screenshot still captures only the visible viewport.

2. Set viewport, DPR, and screenshot scale

Set the context before navigation

Set the viewport on the browser context before opening or navigating the page. Playwright notes that some sites do not expect phones to change size, so setting the viewport before navigation can matter. Configure DPR separately; the documented default is 1, but setting it explicitly makes the capture’s intent clear.

const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 2,
});
const page = await context.newPage();
await page.goto('https://example.com');
await page.screenshot({ path: 'page.png', scale: 'css' });

Here the page lays out at 1440 × 900 CSS pixels and uses DPR 2, while scale: 'css' requests CSS-pixel-sized output. If instead the screenshot uses scale: 'device', output follows device pixels; at DPR 2, that generally means twice as many output pixels in each dimension as CSS-scale capture. The official API describes device scale as producing a pixel per device pixel, so high-DPI screenshots can be larger.

Choose screenshot scale deliberately

  • scale: 'css': choose this when the file should have one output pixel per CSS pixel.
  • scale: 'device': choose this when the file should represent device pixels, such as a high-DPI capture.

Do not rely on an implicit scale when exact output dimensions are part of a test or downstream image-processing step. Set it in the screenshot call.

Set the viewport on an existing page

If a page already exists, use page.setViewportSize(). For predictable site behavior, do this before navigation:

const page = await context.newPage();
await page.setViewportSize({ width: 1280, height: 720 });
await page.goto('https://example.com');
await page.screenshot({ path: 'viewport.png', scale: 'css' });

The Page API documents setViewportSize and recommends setting the size before navigation because some sites do not expect a phone’s size to change.

3. Choose the capture area

Capture only the viewport

Without fullPage, a screenshot captures the visible page area. Its layout dimensions come from the viewport, and its output pixel dimensions also depend on the screenshot scale and DPR.

Capture the full scrollable page

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

fullPage: true captures the entire scrollable document, so the image can be taller than the viewport. It does not correct density or change the page’s CSS viewport width. If the full-page image is too tall, confirm that a full-page capture is intended.

Capture a clipped region

Use clip to capture a rectangle. The rectangle is an area selection, not a substitute for setting the page viewport:

await page.screenshot({
  path: 'region.png',
  clip: { x: 0, y: 0, width: 640, height: 360 },
  scale: 'css',
});

The screenshot API also documents options such as path, type, quality, omitBackground, and animations. For sizing problems, the important controls are viewport, context DPR, screenshot scale, and capture extent (fullPage or clip). See the screenshot API reference for the current option set and format-specific behavior.

4. Configure Playwright Test and device presets

In Playwright Test, set the viewport and DPR in the project’s use settings. A device preset can supply a viewport; place an explicit viewport after the preset spread so your chosen dimensions override it.

// playwright.config.js
const { defineConfig, devices } = require('@playwright/test');

module.exports = defineConfig({
  projects: [
    {
      name: 'desktop-sized capture',
      use: {
        ...devices['Desktop Chrome'],
        viewport: { width: 1280, height: 720 },
        deviceScaleFactor: 1,
      },
    },
  ],
});

This follows the ordering shown in Playwright’s emulation guide. If you use a different device preset, inspect the values it supplies and add explicit overrides for the dimensions and DPR you need. The screenshot call still controls image scale:

const { test } = require('@playwright/test');

test('capture at a defined size', async ({ page }) => {
  await page.goto('https://example.com');
  await page.screenshot({ path: 'page.png', scale: 'css' });
});

5. Set exact output dimensions for common cases

Goal Viewport DPR Screenshot scale Capture extent
1280 × 720 image in CSS-pixel dimensions 1280 × 720 1 css Viewport
High-DPI device-pixel image Chosen CSS dimensions 2 (or intended DPR) device Viewport
Entire document at CSS-pixel scale Chosen layout width and viewport height Explicit value css fullPage: true
One page region Chosen layout viewport Explicit value css or device clip rectangle

For device-scale output, the output dimensions depend on DPR as well as the CSS dimensions being captured. If a precise pixel count matters, calculate for the intended DPR and verify the resulting image dimensions in your own capture pipeline.

6. Troubleshoot a screenshot with the wrong size

Symptom Likely cause Fix
Image has the wrong width or height Viewport differs from the intended CSS-pixel dimensions, or a preset/configuration supplies another viewport. Inspect the effective context or test-project settings. Set explicit width and height; when spreading a device preset, put viewport after the spread.
Image dimensions are larger than expected scale: 'device' produces device-pixel output, and DPR may be greater than 1. Use scale: 'css' for one output pixel per CSS pixel, or keep device scale and account for DPR.
Page content looks mobile or desktop when the opposite was expected The layout viewport is not the intended size, or a preset supplied a different viewport. Set the viewport explicitly before navigation and check preset spread order.
Screenshot is unexpectedly tall fullPage: true captures the scrollable document. Remove fullPage for a viewport capture, or retain it when the full document is required.
Screenshot contains only a small part of the page A clip rectangle limits the captured region. Remove the clip or set its x, y, width, and height to the desired region.
Visual test changes across machines Rendering can vary with OS, browser version, settings, hardware, power source, and headless mode. Keep baseline generation and comparison on aligned browser and host environments; see Playwright visual comparisons.

7. Make screenshot runs reproducible

A fixed viewport and scale make dimensions intentional, but do not guarantee identical rendering on every machine. Playwright documents visual differences from operating system, browser version, settings, hardware, power conditions, and headless mode. Keep the browser and host environment consistent when generating and comparing visual baselines.

Playwright Test’s toHaveScreenshot() waits for two consecutive screenshots to match before comparing with the expectation. This helps avoid comparing during transient visual changes; it cannot remove differences caused by different environments. See the visual comparison guide and PageAssertions API.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF, so you do not need to install or configure a browser for a straightforward capture. Its API docs describe the 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 fs = require('node:fs');
fs.writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot; 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 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. Every feature is on every plan. Sign up for 1,000 free screenshots a month, no card required.

9. Performance, reliability, and cost notes

  • Image size: Device-scale output can contain more pixels than CSS-scale output, especially at higher DPR. Larger images can take more storage and bandwidth in your own pipeline.
  • Full-page captures: Capturing a long document produces a taller image than capturing the viewport. Use it only when the full page is needed.
  • Visual reliability: Pin and align browser and host environments for screenshot baselines. A stable screenshot comparison does not make different rendering environments identical.
  • Playwright cost: Playwright is a browser automation library; this workflow has no per-screenshot API charge specified by the cited sizing documentation. Your runtime, infrastructure, and storage costs depend on where and how you run it.
  • ScreenshotNeo cost: Free includes 1,000 shots/month; Starter is $5 for 3,000, Growth $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.

FAQ

Does fullPage: true fix a screenshot’s pixel density?

No. It changes the capture extent to include the scrollable document. DPR and screenshot scale determine pixel density and output scaling.

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

Use css when output pixels should correspond one-to-one with CSS pixels. Use device when you want output at the emulated device’s pixel density.

Why does a device preset change my dimensions?

Presets can supply viewport settings. In Playwright Test, spread the preset first and put your explicit viewport after it.

Will a fixed viewport make screenshots identical on every computer?

No. Playwright documents rendering differences across browser and host environments. Keep the comparison and baseline environments aligned.