Q8 of 42 · Playwright

How do you take a screenshot in Playwright?

PlaywrightJuniorplaywrightscreenshotvisualfundamentalsjunior

Short answer

Short answer: Use `page.screenshot({ path: 'shot.png' })` for a viewport screenshot or `{ fullPage: true }` for the whole page. `locator.screenshot()` captures a specific element. The runner can also auto-screenshot on failure via the `screenshot` config option.

Detail

Three levels of screenshot in Playwright:

Viewport / full page:

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

Specific element:

await page.getByTestId('summary-card').screenshot({ path: 'summary.png' });

Automatic on failure via config:

use: {
  screenshot: 'only-on-failure',  // 'off' | 'on' | 'only-on-failure'
}

These land in test-results/ and appear in the HTML report.

Visual regression (snapshot comparison) uses a different API:

await expect(page).toHaveScreenshot('home.png');             // full-page baseline
await expect(page.getByTestId('card')).toHaveScreenshot();   // element baseline

The first run writes the baseline; subsequent runs diff. --update-snapshots regenerates baselines when intentional changes are made. Threshold configurable via maxDiffPixels, maxDiffPixelRatio, threshold.

Useful options on screenshot:

  • mask: [Locator] — black out volatile elements (timestamps, ads).
  • animations: 'disabled' — pause animations for stable captures.
  • omitBackground: true — transparent background, useful for components.
  • type: 'png' | 'jpeg', quality for JPEGs.

// EXAMPLE

screenshots.spec.ts

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

test('captures evidence on failure', async ({ page }) => {
  await page.goto('/dashboard');

  // Manual screenshot
  await page.screenshot({ path: 'test-results/dashboard.png', fullPage: true });

  // Element-level
  await page.getByTestId('summary-card').screenshot({
    path: 'test-results/summary.png',
    mask: [page.getByTestId('relative-time')],   // hide volatile time labels
  });
});

test('visual regression on summary card', async ({ page }) => {
  await page.goto('/dashboard');
  await expect(page.getByTestId('summary-card')).toHaveScreenshot('summary.png', {
    maxDiffPixels: 50,
  });
});

// WHAT INTERVIEWERS LOOK FOR

Knowing both manual capture and visual-regression APIs, the `mask` option for volatile elements, and the on-failure auto-capture config.

// COMMON PITFALL

Confusing `page.screenshot()` (saves an image) with `expect(page).toHaveScreenshot()` (does diff comparison) — they're different APIs.