ScreenshotNeo

BlogGuides

Where Do Playwright Screenshots Get Saved?

Find exactly where Playwright screenshots go: direct API paths, test-results artifacts, visual snapshots, attachments, and traces.

By the ScreenshotNeo team1 October 20267 min read

Direct answer: Playwright screenshots do not have one universal folder. The location depends on the API that created the image:

  • page.screenshot() and locator.screenshot() save to the path you provide. A relative path is resolved from the process’s current working directory. With no path, Playwright returns image bytes and writes no file.
  • Playwright Test artifacts normally go under test-results, or the configured outputDir. Each test gets its own subdirectory.
  • expect(page).toHaveScreenshot() uses the configured snapshot path template.
  • testInfo.attach() puts a copy in a reporter-accessible attachment location.
  • Tracing stores screenshots inside the trace, which you inspect with Trace Viewer rather than as an ordinary screenshot file.

Start by identifying which of these mechanisms produced your image.

1. Direct screenshots: the path decides

The direct screenshot APIs are page.screenshot() for a page and locator.screenshot() for one element. Their destination is explicit:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');

// Relative paths start at the process current working directory.
await page.screenshot({ path: 'artifacts/home.png', fullPage: true });

const heading = page.locator('h1');
await heading.screenshot({ path: 'artifacts/heading.png' });

await browser.close();

If the directory does not exist, create it before writing, or use a path whose parent already exists. The path is not relative to the test file or the Playwright config file; it is relative to the process current working directory. See the Page.screenshot API documentation.

No path means no disk file

const bytes = await page.screenshot({ type: 'png' });
// bytes is a Buffer. Save it yourself if needed.
import { writeFile } from 'node:fs/promises';
await writeFile('artifacts/from-buffer.png', bytes);

The API documentation states that when no path is provided, the image is not saved to disk. This commonly explains an apparently missing screenshot.

Useful direct screenshot options

Option Effect
path Writes the image to this file.
fullPage Captures the full scrollable page instead of the viewport.
type Chooses png or jpeg.
quality Sets JPEG quality; it does not apply to PNG.
omitBackground Uses a transparent background where supported.
clip Captures a rectangle in page coordinates.
animations Controls whether CSS/Web Animations run during capture.
caret Controls whether a text caret is visible.
scale Controls whether output uses CSS pixels or device pixels.

2. Playwright Test artifacts: usually test-results

When Playwright Test creates screenshots, videos, or traces as test artifacts, it writes them below the test output directory. If you do not configure one, the documented default is <package.json directory>/test-results. Playwright creates a unique directory for each test, which prevents collisions during parallel runs.

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

test('home page screenshot', async ({ page }, testInfo) => {
  await page.goto('https://example.com');
  const file = testInfo.outputPath('home.png');
  await page.screenshot({ path: file });
  console.log(file);
});

testInfo.outputPath() constructs a path inside the current test’s output directory. testInfo.outputDir tells you that directory directly. Configure the root with outputDir:

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

export default defineConfig({
  outputDir: 'artifacts/playwright',
});

Read the outputDir configuration reference and TestInfo API when locating artifacts in CI or custom runners. The official configuration documentation describes trace files, screenshots, and videos as appearing in the test output directory, typically test-results.

3. Visual regression snapshots use a separate path

expect(page).toHaveScreenshot() is an assertion workflow, not the same as a direct screenshot call. Its baseline images are controlled by snapshot path settings. A relative template resolves from the Playwright config directory.

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

test('visual baseline', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveScreenshot('home.png');
});

Inspect snapshotPathTemplate in playwright.config.*, or an assertion-specific pathTemplate, to find the actual location. Templates can include project, test, browser, and snapshot-name segments, so the resulting path may not be beside the test source. See the visual comparisons documentation.

4. Attachments appear in reports

testInfo.attach() copies a file or supplied body into a reporter-accessible attachment. The report can show it even when your original screenshot was created in memory or saved somewhere unexpected.

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

test('attach screenshot', async ({ page }, testInfo) => {
  await page.goto('https://example.com');
  const image = await page.screenshot();
  await testInfo.attach('homepage', {
    body: image,
    contentType: 'image/png',
  });
});

Look in the test report’s attachments area. Do not assume that an attachment is a standalone file next to the test. The testInfo.attach reference documents the copy operation and attachment metadata.

5. Trace screenshots are inside the trace

Tracing can record screenshots for its visual timeline. Those frames are stored in the trace archive and viewed with Trace Viewer; they are not automatically named files from page.screenshot({ path }).

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://example.com');
await context.tracing.stop({ path: 'artifacts/trace.zip' });
await browser.close();

Open the resulting trace with the Playwright Trace Viewer. See the Trace Viewer documentation for the inspection workflow.

6. A reliable way to locate a missing screenshot

  1. Find the producer. Search for page.screenshot, locator.screenshot, toHaveScreenshot, testInfo.attach, and tracing configuration.
  2. Check for path. If a direct call has no path, capture the returned buffer or add a path.
  3. Resolve relative paths from the working directory. Print process.cwd() in Node.js to verify where the process started.
  4. Inspect Playwright Test configuration. Check outputDir; if absent, check the package directory’s test-results.
  5. Check per-test output paths. Log testInfo.outputDir or use testInfo.outputPath('name.png').
  6. For visual assertions, inspect snapshot templates. Check both global snapshotPathTemplate and assertion-level templates.
  7. For attachments, open the report. The report may contain the image even when no expected disk file exists.
  8. For traces, open the trace archive. Timeline screenshots are trace content.

7. Common errors and fixes

Symptom Cause Fix
No file after await page.screenshot() No path was supplied. Pass path or write the returned buffer.
File is in an unexpected folder Relative paths use the process working directory. Log process.cwd() or use an absolute/constructed path.
Cannot find test screenshot in the repository Artifacts are under outputDir and a per-test subdirectory. Inspect config, testInfo.outputDir, and the test report.
Baseline is not beside the test Snapshot templates control assertion paths. Inspect snapshotPathTemplate and pathTemplate.
Report shows an image but no local screenshot The image was attached to the reporter. Use the report’s attachment location or save a copy explicitly.
Trace Viewer shows frames but no PNG files Frames are stored inside the trace archive. Open the trace; create a direct screenshot if a separate file is required.
Parallel tests overwrite files Multiple tests use the same explicit path. Use testInfo.outputPath() or include test/project identifiers in names.
Screenshot write fails in CI The parent directory is missing or not writable. Create the directory, choose a writable output directory, and log the resolved path.

8. CI, performance, and reliability considerations

  • Use testInfo.outputPath() for test artifacts so parallel workers receive isolated paths.
  • Keep screenshots in the configured output directory when CI collects artifacts; avoid writing to ephemeral working directories unless the CI job preserves them.
  • Full-page screenshots can be larger and slower than viewport captures. Capture only the needed element or viewport for routine diagnostics.
  • PNG preserves exact pixels and is useful for visual assertions; JPEG is smaller but introduces compression differences.
  • For deterministic snapshots, control viewport, device scale, fonts, animations, data, and network-dependent content in your test setup.
  • Do not confuse a report attachment or trace frame with a durable file. Export or save a standalone image when another job must consume it.
  • Clean old output directories in CI according to your retention policy; Playwright’s output location tells you where those artifacts accumulate.

9. Or skip the browser setup

If you need a clean screenshot file from a URL rather than a browser test artifact, ScreenshotNeo provides a GET endpoint that returns PNG, JPEG, WebP, or PDF. Its capture service accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. It also provides an MCP server for AI agents with take_screenshot, get_page_info, and capture_pdf tools.

See the ScreenshotNeo API documentation for all options.

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(`Screenshot failed: ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', data));

Every feature is available on every plan. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

10. FAQ

Does Playwright save screenshots automatically?

No. A direct screenshot call needs a path; otherwise it returns bytes. Playwright Test may save configured artifacts under its output directory.

Is test-results always the folder?

No. It is the documented default when outputDir is unset. A project can configure another output directory.

Why is my screenshot next to neither the test nor config?

Direct relative paths use the process current working directory, while snapshots use their path template and test artifacts use the output directory.

Where are screenshots from toHaveScreenshot()?

Inspect snapshotPathTemplate and any assertion-specific path template in the active configuration.

Can a trace screenshot be used as a PNG file?

It is stored in the trace and viewed in Trace Viewer. Take a separate screenshot with an explicit path when another tool needs a standalone image.