Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use page.screenshot() when you need an image at a precise step, and configure Playwright Test’s use options when every test (or every failure/retry) should produce artifacts. Record videos either with the test runner’s video mode or with recordVideo on a manually created browser context. Screenshots and videos are disabled by default in Playwright Test.

This guide covers runnable TypeScript examples, artifact paths, video lifecycle, visual-regression baselines, sizing, failure modes, and an API alternative when you do not want to maintain a browser environment.

Capture a screenshot at an exact point

In any Playwright test, call await page.screenshot() after the page has reached the state you want to preserve. The call waits for the screenshot operation to finish before the test continues.

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

test('checkout confirmation', async ({ page }) => {
  await page.goto('https://example.com/checkout');
  await page.getByRole('button', { name: 'Place order' }).click();
  await expect(page.getByRole('heading', { name: 'Thank you' })).toBeVisible();

  await page.screenshot({ path: 'artifacts/confirmation.png', fullPage: true });
});

path can be relative to the process working directory. For parallel tests, prefer Playwright Test’s per-test output directory so files cannot overwrite one another:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { test } from '@playwright/test';

test('unique artifact path', async ({ page }, testInfo) => {
  await page.goto('https://example.com');
  const file = testInfo.outputPath('homepage.png');
  await page.screenshot({ path: file, fullPage: true });
});

testInfo.outputPath() creates a path under that test’s output folder (normally inside test-results), which is safer for retries and workers.

Useful screenshot options

  • Full page: fullPage: true captures the full scrollable document instead of only the viewport.
  • Element only: locator.screenshot({ path: 'card.png' }) captures a specific element after it is located.
  • Format: use a .png, .jpeg, or other supported extension; JPEG supports quality.
  • Masking: pass mask: [locator] to cover dynamic regions in supported Playwright versions.
  • Animation control: animations: 'disabled' can make captures more stable.

Wait for the state that matters rather than relying on a fixed sleep. Assertions such as await expect(locator).toBeVisible() provide a condition that explains why the capture occurred.

Configure automatic screenshots in Playwright Test

Set the screenshot option in playwright.config.ts. The official configuration documents screenshots as off by default and supports modes that limit captures to useful debugging runs.

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

export default defineConfig({
  use: {
    screenshot: 'only-on-failure'
  }
});

Choose the mode that matches your artifact policy:

Mode What it does When to use it
'off' No automatic screenshot Lowest artifact volume; default behavior
'on' Captures every test run Auditing, demonstrations, or complete visual history
'only-on-failure' Keeps a screenshot for failed tests General failure debugging
'on-first-failure' Captures on the first failure attempt Projects where retries should not create duplicate images

Automatic screenshots are written with the other test artifacts, usually beneath test-results. Your reporter and CI system may package or expose that directory differently, so inspect the reporter output when locating files in a pipeline.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Record videos with Playwright Test

Configure video in the same use block. Recording and retention are separate concerns: a mode can record retries while retaining only the runs that help diagnose failures.

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

export default defineConfig({
  use: {
    video: 'on-first-retry'
  }
});
Video mode Behavior Typical reason
'off' No recording Default or lowest storage use
'on' Record every run Complete session history
'retain-on-failure' Record runs and retain recordings for failures Debug failures without keeping successful-run videos
'on-first-retry' Record the first retry Capture intermittent failures with less storage
'on-all-retries' Record every retry Compare behavior across repeated attempts
'retain-on-first-failure' Retain the first failing recording Keep one representative failure artifact
'retain-on-failure-and-retries' Retain failure and retry recordings Investigate flaky behavior in depth

The exact set of modes is defined by the current TestOptions API. Video files normally appear in the test output directory alongside traces and screenshots.

Record a video manually with a browser context

When you are using Playwright Library rather than Playwright Test, create a context with recordVideo. The video is finalized only after the page or context closes; do not read its path immediately after navigation.

import { chromium } from 'playwright';

const browser = await chromium.launch();
const context = await browser.newContext({
  recordVideo: { dir: 'videos/' },
  viewport: { width: 1280, height: 720 }
});
const page = await context.newPage();

await page.goto('https://example.com');
await page.getByRole('link', { name: 'More information' }).click();

await context.close(); // finalizes the recording
await browser.close();

The official video guide documents retrieving a page’s video() object and its path after closure. Closing the context in a finally block is important when a test throws:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const context = await browser.newContext({ recordVideo: { dir: 'videos/' } });
try {
  const page = await context.newPage();
  await page.goto('https://example.com');
  // test actions
} finally {
  await context.close();
}

Control video dimensions and annotations

Set an explicit viewport when reproducible dimensions matter. The video guide states that, unless configured otherwise, Playwright scales the viewport to fit within 800×800; when no viewport is set, the documented default video size is 800×450. A large viewport can therefore be downscaled in the recording.

const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  recordVideo: {
    dir: 'videos/',
    size: { width: 1280, height: 800 }
  }
});

Playwright also supports action annotations and an overlay containing test information. The documented default annotation duration is 500 milliseconds. These options and defaults can change between Playwright releases, so verify them in the version of the video documentation that your project uses.

Use screenshots as visual-regression baselines

expect(page).toHaveScreenshot() turns a screenshot into an assertion. On the first run, Playwright creates a reference image; subsequent runs compare the current rendering with that baseline.

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

test('homepage remains visually stable', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveScreenshot('homepage.png', {
    fullPage: true
  });
});

PNG is the default snapshot format. Use a filename ending in .webp when you want WebP, which Playwright documents as a lossless alternative. Store baselines in version control and review intentional visual changes as code changes.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Rendering can vary with the host operating system, browser version, browser settings, hardware, power source, and headless mode. Generate and compare baselines in the same environment (for example, the same CI image) to reduce false differences. Avoid putting clocks, random IDs, live advertisements, or other changing content in a baseline; mask or stub those regions instead. See the visual comparisons guide for the current comparison behavior.

Choose the right capture strategy

Goal Recommended approach Trade-off
One diagnostic image Explicit page.screenshot() You must choose the correct state and path
Every test has an image screenshot: 'on' More files and storage
Failure evidence screenshot: 'only-on-failure' Successful runs have no automatic image
Videos for flaky tests video: 'on-first-retry' or a retain-on-failure mode Only selected attempts are available
Full session history video: 'on' Highest CPU, disk, and upload cost
Pixel-level regression toHaveScreenshot() Requires stable environments and baseline review
Standalone script recordVideo on newContext() You must close the context to finalize video

Troubleshoot missing or unexpected artifacts

No screenshot appears

  • Confirm the test reached the screenshot call; an earlier exception prevents it.
  • Check that the configured mode is not 'off' and that a failure-only mode actually saw a failure.
  • Print or inspect testInfo.outputPath() and the reporter’s artifact links instead of assuming the current directory.
  • For a manual screenshot, ensure the destination directory exists or use a path under testInfo.outputPath().

Video file is missing or zero bytes

  • Close the browser context before reading page.video().path() or uploading the file.
  • Ensure the context, not just an individual page, was closed in error paths.
  • Check that your selected video mode records the run you are examining; retry-only modes do not record the initial attempt.

Visual test fails only on CI

  • Use the same browser version, operating-system image, viewport, color scheme, and headless setting for baseline generation and comparison.
  • Wait for fonts, images, and application data before the assertion.
  • Mask dynamic content or make it deterministic.
  • Review the generated diff and update the baseline only when the design change is intentional.

Full-page capture is clipped or differs between runs

Wait for lazy content to load and ensure the page has reached a stable layout before calling fullPage: true. Fixed headers, animations, and late font swaps can change the final bitmap; disable animations or assert on the relevant content first.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you need a clean website image rather than a browser test artifact, ScreenshotNeo provides a GET-based screenshot API and MCP server. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status.

One request returns PNG, JPEG, WebP, or a PDF. The service supports full-page shots with lazy images, CSS-selector element capture, device presets and custom viewports, retina scale, dark mode, custom CSS and JavaScript, clicks, waits, blocked requests, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

cURL

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

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

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(`${res.status} ${res.statusText}`);
const data = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', data);

See the ScreenshotNeo documentation for parameters and response headers. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account.

Operational and cost considerations

  • Images consume less storage and processing time than videos; enable video only for runs where motion explains a failure.
  • Failure-only and retry modes reduce CI artifact uploads while preserving evidence for broken or flaky tests.
  • Use deterministic test data, stable browser versions, and explicit viewports for reproducible screenshots.
  • Keep secrets out of screenshots and videos: redact sensitive fields or use test accounts.
  • Set retention policies in your CI artifact store; Playwright’s capture mode controls creation and retention within the test run, not your external storage lifecycle.

Frequently Asked Questions

Where does Playwright save automatic screenshots?

They are normally placed in the test output directory, commonly under test-results. Use your reporter’s artifact links or testInfo.outputPath() to obtain the exact path.

Can I capture only one element instead of the whole page?

Yes. Locate it and call await locator.screenshot({ path: 'element.png' }); this captures the element’s rendered box.

Why must a video context be closed?

Playwright finalizes and writes the recording when the page or browser context closes. Read or upload the video after await context.close().

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Are screenshots and videos enabled by default?

No. Playwright Test leaves both off until you set the corresponding use options or call a capture API yourself.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.