ScreenshotNeo

BlogGuides

Playwright HTML Reports With Screenshots

Generate Playwright HTML reports, retain screenshots and traces in CI, and debug failures with a repeatable workflow.

By the ScreenshotNeo team1 October 20267 min read

Direct answer: run Playwright with the HTML reporter, retain the report directory as a CI artifact, and enable traces on retries or failures. Screenshots captured by tests appear as attachments; trace screenshots appear as a film strip in Trace Viewer.

npx playwright test --reporter=html
npx playwright show-report

The first command runs your suite and writes an HTML report. The second starts a local server so you can inspect test status, steps, errors, attachments, and trace links. Playwright’s HTML reporter documentation and Trace Viewer documentation describe these commands and the inspection workflow.

What the HTML report shows

The report groups the tests that ran and lets you filter by passed, failed, flaky, and skipped status. It also shows the browser project and test duration. Open a test to see its error, individual steps, and links to traces or other attachments.

Report signal What it helps you determine
Passed, failed, flaky, skipped Whether the failure is consistent, retry-related, or caused by a skipped condition
Browser/project Whether the problem is browser-specific
Duration Whether a timeout or slow step is involved
Retry state Whether the test only failed on its first attempt
Screenshot attachment What the page looked like at the point you captured it
Trace attachment What happened before, during, and after each recorded action

Configure screenshots in a Playwright test

Use page.screenshot() when you want a deliberate image at a specific point. The following test is runnable in a standard Playwright Test project.

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

test('checkout page has the expected heading', async ({ page }, testInfo) => {
  await page.goto('https://example.com/checkout', { waitUntil: 'networkidle' });

  await expect(page.getByRole('heading', { name: 'Checkout' })).toBeVisible();

  const path = testInfo.outputPath('checkout.png');
  await page.screenshot({ path, fullPage: true });
  await testInfo.attach('checkout screenshot', {
    path,
    contentType: 'image/png',
  });
});

testInfo.outputPath() keeps the file in the test’s output directory, and testInfo.attach() makes it visible from the report. Attach a screenshot after the state you want to diagnose: after navigation, after a form submission, or immediately after an assertion fails inside a catch block.

Capture a screenshot when an assertion fails

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

test('profile page', async ({ page }, testInfo) => {
  await page.goto('https://example.com/profile');

  try {
    await expect(page.getByRole('heading', { name: 'Account' })).toBeVisible();
  } catch (error) {
    const path = testInfo.outputPath('failure.png');
    await page.screenshot({ path, fullPage: true });
    await testInfo.attach('failure screenshot', {
      path,
      contentType: 'image/png',
    });
    throw error;
  }
});

Use Playwright’s built-in failure screenshots

For routine failure evidence, configure screenshots in playwright.config.ts. This avoids repeating screenshot code in every test.

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

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

Use 'only-on-failure' to limit artifact volume. Choose 'on' when every test needs an image, or 'off' when screenshots are not useful for a project.

Keep screenshots and traces in CI

Traces record a screencast and expose a film strip in Trace Viewer. Hovering the film strip magnifies the image for an action or state. The viewer also exposes before, action, and after DOM snapshots, the locator and source location, logs, network requests, console output, browser and viewport metadata, and attachments such as expected, actual, and diff images.

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

export default defineConfig({
  retries: 2,
  reporter: [['html', { outputFolder: 'playwright-report', open: 'never' }]],
  use: {
    screenshot: 'only-on-failure',
    trace: 'on-first-retry',
  },
});

trace: 'on-first-retry' records a trace when a test is retried for the first time. If your project does not use retries, use trace: 'retain-on-failure' so failed tests keep their traces. trace: 'on' records every test and is performance-heavy, so reserve it for targeted debugging.

Run locally and inspect the report

npx playwright test
npx playwright show-report playwright-report

If you used the command-line reporter override, run npx playwright test --reporter=html first, then npx playwright show-report. In CI, upload the complete playwright-report directory and the test output directory as artifacts. Keep the report and its referenced attachments together; moving only the HTML files breaks links to images and traces.

How to debug a failure from the report

  1. Filter the report to failed or flaky tests.
  2. Check the browser, duration, retry state, and test steps.
  3. Open the screenshot attachment to see the rendered state.
  4. Open the trace icon or the test’s Traces tab.
  5. Move through the action timeline and compare before, action, and after snapshots.
  6. Inspect locator source, network requests, console output, and metadata.
  7. Compare expected, actual, and diff images when the test includes a visual assertion.

This sequence separates common causes: a browser-only defect, a timing problem, a changed locator, a failed network request, or a visual regression. A screenshot tells you what was visible; a trace explains which action and page state led there.

CI workflow checklist

  • Set the HTML reporter output folder explicitly.
  • Enable screenshot: 'only-on-failure' for routine evidence.
  • Use on-first-retry with retries, or retain-on-failure without retries.
  • Upload the report directory and test-results directory as CI artifacts.
  • Keep the same Playwright version and browser binaries across local and CI runs.
  • Open the report from an artifact workspace with npx playwright show-report.
  • Use trace: 'on' only for a short, targeted debugging run.

Common errors and fixes

Symptom Likely cause Fix
show-report cannot find a report The test run used a different output folder or did not finish Run the test command again and pass the exact folder: npx playwright show-report playwright-report.
Images are missing from an uploaded report Only HTML files were uploaded Upload the entire report and test-results directories, preserving their relative paths.
No trace link appears Trace mode was disabled or the test never retried/failed under that mode Use on-first-retry with retries, retain-on-failure, or temporarily use on.
Screenshot shows a loading state The capture ran before the page reached the required state Wait for a role, locator, URL, or application condition before calling screenshot(); avoid arbitrary sleeps where a locator assertion is available.
CI report is too large Every test records screenshots and full traces Use failure-only screenshots, retain traces on failure, and run full tracing only for a focused investigation.
Visual differences appear only in CI Browser, viewport, fonts, or timing differ Compare the report’s browser and viewport metadata, then align browser versions, viewport settings, and readiness checks.

Performance, reliability, and cost considerations

Screenshots and traces add disk usage and can increase test overhead. Recording every trace is the heaviest option; failure-only screenshots and retry or failure traces keep routine runs smaller. Full-page screenshots can be substantially larger than viewport captures, especially on long pages.

For reliable evidence, capture after a deterministic condition such as a visible locator or completed navigation. A trace is more useful than a screenshot when you need to reconstruct the exact action sequence, network activity, and console state. Keep artifact retention long enough for the team to investigate failures, then apply your CI platform’s retention policy.

The Playwright report itself is a software artifact; its storage cost depends on how many tests, screenshots, videos, and traces you retain. The research sources do not provide a universal screenshot-size or performance benchmark, so size and runtime should be measured in your own suite.

Or skip the browser setup

If you need clean screenshots of pages outside your Playwright run, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Each step can be turned off.

Only clean shots are billed. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response identifies the result with X-Page-Verdict and X-Billed headers. An MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

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

See the ScreenshotNeo API docs for the full option set, including full-page capture with lazy images loaded, CSS element capture, dark mode, device presets and custom viewports, retina scale, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching, signed links, asynchronous jobs, webhooks, bulk capture, usage, and the OpenAPI specification.

Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 shots. Start with the free ScreenshotNeo account.

FAQ

Where does Playwright put the HTML report?

When you use the HTML reporter, Playwright writes a report directory for the run. Set outputFolder in the reporter configuration when you need a predictable path for CI artifacts.

Can a report contain both screenshots and traces?

Yes. Screenshots are attachments, while traces open in Trace Viewer with a film strip and diagnostic panels. Configure each independently.

Should every test record a trace?

Usually no. Use on-first-retry or retain-on-failure for normal CI and switch to on for focused investigations.

How do I inspect a report downloaded from CI?

Extract the complete artifact, enter the directory containing the report, and run npx playwright show-report. Preserve the attached files beside the report.

Can ScreenshotNeo replace Playwright traces?

No. ScreenshotNeo supplies page images or PDFs through an API and MCP tools. Playwright traces remain the right artifact for action timelines, DOM snapshots, network requests, and console details.