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

Use Playwright Test’s built-in page fixture and call await page.screenshot() at the exact state you want to inspect. Pass a path to save an image, or keep the returned bytes and attach them with testInfo.attach(). Use a trace when you need actions, DOM snapshots, and network context; enable video only when a replay of the run is useful. Playwright Test creates an isolated browser context for each test, while manually created contexts must be closed so video and other artifacts are finalized.

How do I take a screenshot in a Playwright test?

Install Playwright Test and create a test file such as tests/capture.spec.ts:

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

test('capture the landing page', async ({ page }) => {
  await page.goto('https://playwright.dev');
  await expect(page).toHaveTitle(/Playwright/);
  await page.screenshot({ path: 'artifacts/landing.png', fullPage: true });
});

The runner supplies page through its built-in fixture. The screenshot is taken after navigation and the title assertion, so the captured state is meaningful rather than an intermediate loading screen. page.screenshot() returns a buffer when no path is supplied; providing path writes the image to that location. See the Page API documentation for the complete option set.

Save a viewport or full-page image

  • await page.screenshot({ path: 'shot.png' }) captures the current viewport.
  • fullPage: true extends the image through the page’s full scrollable height.
  • Choose an extension such as .png or .jpeg that matches the format you need; use the documented screenshot options for quality and masking controls.

Capture after a stable locator is visible when the page contains asynchronous content:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.goto('https://example.com');
await page.getByRole('main').waitFor();
await page.screenshot({ path: 'artifacts/ready.png' });

In a Playwright Test project, artifact paths should normally be placed under a directory you control or one configured for your CI artifact collection. The runner’s result directory is preferable when you want reporters to associate files with a test rather than leave files in the repository.

How do I attach a screenshot to the Playwright Test result?

Call page.screenshot() without a path, then pass the returned bytes to testInfo.attach():

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

test('attach a visual checkpoint', async ({ page }, testInfo) => {
  await page.goto('https://playwright.dev');
  await expect(page.getByRole('heading', { name: 'Playwright enables reliable end-to-end testing for modern web apps.' })).toBeVisible();

  const screenshot = await page.screenshot();
  await testInfo.attach('homepage', {
    body: screenshot,
    contentType: 'image/png',
  });
});

TestInfo.attach() copies the attachment to a location available to reporters. Supplying body with the screenshot buffer avoids a temporary file, while contentType tells the reporter how to display it. The API is documented in the TestInfo reference.

Attach only when a test fails

A screenshot is most useful as failure evidence when routine passing artifacts would create noise. This pattern captures the current page in a failure handler:

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('checkout flow', async ({ page }, testInfo) => {
  await page.goto('https://example.com/checkout');
  try {
    await page.getByRole('button', { name: 'Pay' }).click();
    await page.getByText('Payment complete').waitFor();
  } catch (error) {
    await testInfo.attach('failure-state', {
      body: await page.screenshot(),
      contentType: 'image/png',
    });
    throw error;
  }
});

For broad failure handling, configure Playwright Test’s reporter and artifact policy rather than duplicating this wrapper in every test. Keep the attachment name stable and descriptive so HTML or CI reporters are easy to scan.

When should I use a screenshot, trace, or video?

Artifact Best for Important behavior
Screenshot One visual state, such as a layout checkpoint or failure screen Small, direct output from page.screenshot(); save it or attach its bytes
Trace Explaining a failed sequence of actions Can include action details, locator data, durations, DOM snapshots, and a screenshot film strip; recording every test can be performance heavy
Video A replay-like visual record, especially for intermittent failures Opt-in and finalized only after the page or browser context closes

Use tracing when the image cannot explain the failure

A screenshot shows one moment. A trace preserves the path to that moment and can reveal which locator, request, snapshot, or timing caused the problem. For Playwright Test, configure tracing in the test-runner configuration so assertion context is retained:

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

export default defineConfig({
  use: {
    trace: 'retain-on-failure',
  },
});

Open the resulting trace with the Trace Viewer. It exposes action details, locator information, action duration, source location, DOM snapshots, and a film strip when screenshots are enabled. The Trace Viewer guide describes the available retention modes.

Do not confuse runner tracing with library tracing

Standalone Playwright scripts can use browserContext.tracing directly:

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

const browser = await chromium.launch();
const context = await browser.newContext();
await context.tracing.start({ screenshots: true, snapshots: true });
const page = await context.newPage();
await page.goto('https://playwright.dev');
await context.tracing.stop({ path: 'artifacts/trace.zip' });
await context.close();
await browser.close();

Direct context tracing records browser operations and network activity, but it does not record test assertions. If assertion-level failure context matters, use Playwright Test configuration instead. The limitation is documented in the Tracing API.

Record video deliberately

Video recording is off by default. Select a mode in playwright.config.ts:

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

export default defineConfig({
  use: {
    video: 'retain-on-failure',
    // Alternatives include: 'on', 'on-first-retry', and 'retain-on-first-failure'.
  },
});

Use retain-on-failure when ordinary passing runs do not need videos. Choose on-first-retry when intermittent failures are the main concern. The exact supported modes and their retention behavior are listed in the Videos guide.

How does video behave with a manually created context?

When you use the library rather than the Test runner, enable recording while creating the context and close that context before reading the file:

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

const browser = await chromium.launch();
const context = await browser.newContext({
  recordVideo: { dir: 'artifacts/videos' },
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await page.waitForTimeout(500);
await context.close(); // finalizes the video
await browser.close();

Attempting to inspect the recording before context.close() is premature because the video is finalized at context closure. The browser API documents explicit context lifecycle and video setup at Browser API.

Why browser-context isolation matters for captures

Playwright Test gives each test its own browser context, including separate cookies and storage. The isolation described in the browser-context guide prevents one test’s login or local-storage state from leaking into another and makes screenshots reproducible. The built-in page fixture belongs to that isolated context; you normally do not need to launch a browser or create a context in each test. Fixture setup and teardown are covered in the fixtures guide.

Standalone scripts have no such automatic lifecycle. Create a context explicitly, perform captures, close the context, and only then close the browser. If a screenshot unexpectedly shows a logged-out or first-visit state, check whether the test is using the intended fixture, storage state, viewport, and project configuration.

Reliable capture workflow

  1. Choose the artifact. Use a screenshot for a single state, a trace for action-level diagnosis, and video for replaying visual timing.
  2. Wait for the right state. Prefer locator assertions or waits for application readiness over arbitrary delays.
  3. Capture through the runner fixture. In a test, use the supplied page; do not create an extra context unless the test specifically requires it.
  4. Retain intentionally. Attach buffers for reporter visibility, save files for external processing, and retain traces or videos mainly on failures and retries.
  5. Close manually created contexts. This is required to finalize video and other context-owned artifacts.
  6. Collect artifacts in CI. Configure your CI system to preserve the Playwright result directory or the paths you selected.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting common capture problems

The screenshot is blank or taken too early

Cause: navigation or application rendering has not reached the state you intended. Fix: wait for a meaningful locator or assertion, and verify that the URL and page state are correct before calling screenshot(). Avoid relying solely on a fixed timeout.

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

The attachment does not appear in the report

Cause: the buffer was not passed to testInfo.attach(), or the content type is missing or incorrect. Fix: pass body: await page.screenshot() and contentType: 'image/png'; use a reporter that displays attachments.

The trace lacks assertion details

Cause: tracing was started through browserContext.tracing in a standalone script. Fix: configure the Playwright Test trace option when you need the runner’s assertion-aware failure workflow.

The video file is missing or incomplete

Cause: video recording is opt-in, or the manually created context was not closed. Fix: set a supported video mode in runner configuration, or use recordVideo and await context.close() before reading artifacts.

Tests influence one another

Cause: shared state in a manually reused context, rather than Playwright Test’s isolated fixture. Fix: use the built-in page fixture for independent tests, or create and close a fresh context for each standalone scenario.

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

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server when you need a rendered URL without maintaining Playwright browser-launch code. Its capture endpoint accepts a URL and returns PNG, JPEG, WebP, or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.

One cURL request:

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)
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}`);

See the ScreenshotNeo documentation for options including full-page capture, CSS selectors, device and viewport settings, dark mode, custom JavaScript and CSS, waits, request blocking, cookies, headers, geolocation, PDFs, caching, signed links, asynchronous jobs, bulk capture, and the MCP tools take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000. Sign up free.

FAQ

Does page.screenshot() return an image buffer?

Yes. Without path, it returns screenshot bytes that you can attach or process in memory.

Are Playwright Test videos enabled automatically?

No. Video recording is off by default and must be selected through the video configuration option.

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

Can a trace replace a screenshot?

They serve different purposes: a trace explains a sequence and its context, while a screenshot records one visual state.

Frequently Asked Questions

Does page.screenshot() return an image buffer?

Yes. Without path, it returns screenshot bytes that you can attach or process in memory.

Are Playwright Test videos enabled automatically?

No. Video recording is off by default and must be selected through the video configuration option.

Can a trace replace a screenshot?

They serve different purposes: a trace explains a sequence and its context, while a screenshot records one visual state.

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

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.