How to Configure the Playwright Screenshots Folder
Configure Playwright outputDir, test screenshots, and visual baselines without mixing up their different folder settings.
Direct answer: Playwright has three different screenshot locations. Set outputDir for test-run artifacts such as failure screenshots, videos, and traces. For a screenshot created by page.screenshot(), write to testInfo.outputPath(). For expect(page).toHaveScreenshot() visual baselines, configure snapshotPathTemplate or the assertion-specific expect.toHaveScreenshot.pathTemplate.
These settings solve different path problems and should not be exchanged for one another.
Choose the folder setting that matches your screenshot
| What creates the file? | Use | Typical result |
|---|---|---|
| Automatic test artifacts | outputDir |
Failure screenshots, videos, traces, and other run output |
| Your test code | testInfo.outputPath() |
A file inside the current test’s isolated output directory |
| Visual comparison assertions | snapshotPathTemplate or expect.toHaveScreenshot.pathTemplate |
Expected screenshot baselines |
1. Configure the Playwright test artifact folder
Put outputDir in playwright.config.ts. The documented default is <package.json-directory>/test-results. The use.screenshot option controls automatic screenshot capture.
import { defineConfig } from '@playwright/test';
export default defineConfig({
outputDir: './artifacts',
use: {
screenshot: 'only-on-failure',
},
});
The valid screenshot modes are:
'off'— do not capture automatic screenshots.'on'— capture screenshots for every test.'only-on-failure'— capture screenshots when a test fails.
Playwright cleans outputDir at the start of a run. It creates a unique subdirectory for each test, which prevents parallel tests from writing to the same test folder. See the Playwright TestConfig API and use options documentation.
Keep videos and traces with the same run
import { defineConfig } from '@playwright/test';
export default defineConfig({
outputDir: './artifacts',
use: {
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'retain-on-failure',
},
});
These files are run artifacts, so they follow the test output directory rather than the visual snapshot directory.
2. Save an explicit screenshot from test code
When your test calls page.screenshot(), use testInfo.outputPath(). This keeps the file inside the current test’s output directory and preserves Playwright’s per-test isolation.
import { test } from '@playwright/test';
test('capture page', async ({ page }, testInfo) => {
await page.goto('https://example.com');
await page.screenshot({
path: testInfo.outputPath('screenshots/page.png'),
fullPage: true,
});
});
The resolved path must remain inside the current test’s output directory. Use nested names such as screenshots/page.png to organize files without bypassing test isolation.
Capture an element instead of the whole page
import { test } from '@playwright/test';
test('capture checkout summary', async ({ page }, testInfo) => {
await page.goto('https://example.com/checkout');
await page.locator('[data-testid="checkout-summary"]').screenshot({
path: testInfo.outputPath('screenshots/checkout-summary.png'),
});
});
Use a deterministic name for multiple screenshots
import { test } from '@playwright/test';
test('capture states', async ({ page }, testInfo) => {
await page.goto('https://example.com');
await page.screenshot({
path: testInfo.outputPath('screenshots/initial.png'),
});
await page.getByRole('button', { name: 'Open menu' }).click();
await page.screenshot({
path: testInfo.outputPath('screenshots/menu-open.png'),
});
});
For the helper API and its path rules, see the Playwright TestInfo API.
3. Move visual comparison baselines
expect(page).toHaveScreenshot() writes expected images as snapshot files. Configure their layout with snapshotPathTemplate.
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
});
A relative template is resolved relative to the configuration directory. Useful tokens include {testDir}, {testFilePath}, {projectName}, {arg}, and {ext}.
Use a custom folder only for screenshot assertions
import { defineConfig } from '@playwright/test';
export default defineConfig({
expect: {
toHaveScreenshot: {
pathTemplate: '{testDir}/__screenshots__{/projectName}/{testFilePath}/{arg}{ext}',
},
},
});
The optional slash form {/projectName} adds the slash only when the token has a value. This is useful when multiple projects need separate baseline folders.
import { test, expect } from '@playwright/test';
test('homepage matches the baseline', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveScreenshot('homepage.png');
});
Do not use snapshotDir for new configurations. Playwright marks it discouraged and points users to snapshotPathTemplate. See the visual comparisons guide and the TestConfig API.
4. Find the path Playwright will use
Use testInfo.outputPath() for arbitrary files in the current test’s output directory. For assertion baselines, use testInfo.snapshotPath().
import { test } from '@playwright/test';
test('print generated paths', async ({}, testInfo) => {
console.log('artifact:', testInfo.outputPath('screenshots/debug.png'));
console.log('baseline:', testInfo.snapshotPath('homepage.png'));
});
The kind option on testInfo.snapshotPath() selects the screenshot, aria, or generic snapshot template. The API reference documents that option as available from Playwright v1.53.
5. A complete configuration example
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
outputDir: './artifacts',
snapshotPathTemplate: '{testDir}/__screenshots__/{projectName}/{testFilePath}/{arg}{ext}',
use: {
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'mobile',
use: { ...devices['iPhone 13'] },
},
],
});
This arrangement keeps temporary run output in artifacts while placing visual baselines under tests/__screenshots__, separated by project.
6. Common mistakes and fixes
| Symptom | Cause | Fix |
|---|---|---|
Failure screenshots still appear under test-results |
outputDir was not changed in the loaded config. |
Set outputDir in the active playwright.config.ts and confirm the command is using that file. |
| A manual screenshot is outside the test folder | A hard-coded path was passed to page.screenshot(). |
Build the path with testInfo.outputPath(). |
| Baselines remain in the default snapshot layout | Only outputDir was changed. |
Configure snapshotPathTemplate or expect.toHaveScreenshot.pathTemplate. |
| Parallel tests overwrite files | Tests share a manually chosen directory and filename. | Use Playwright’s per-test testInfo.outputPath() or include test/project tokens in the snapshot template. |
| The output folder is empty after starting a run | Playwright cleans outputDir at the start. |
Copy or archive artifacts after the run if they must persist across runs. |
| A template path is rejected | The path resolves outside the intended directory or contains unsupported tokens. | Use documented tokens and verify the resolved path; keep templates relative to the config directory where appropriate. |
| Different projects use the same baseline | {projectName} is missing from the template. |
Add {projectName}, or use the optional {/projectName} form. |
7. CI, retention, and reliability
- Expect
outputDirto be cleaned on every run. Upload artifacts to CI storage after the test command completes. - Keep visual baselines in version control when they are reviewed as part of pull requests.
- Separate projects and browsers in baseline paths to avoid accidental comparisons across viewport or rendering configurations.
- Use
only-on-failurewhen automatic screenshots are diagnostic evidence rather than test data. - Use unique output paths for explicit screenshots in retries and parallel workers.
testInfo.outputPath()supplies the test-scoped isolation. - Do not treat a baseline as a run artifact: baselines should be stable and reviewable, while traces, videos, and failure screenshots are disposable diagnostics.
8. Performance and storage considerations
Full-page screenshots, videos, and traces consume more disk space than viewport screenshots. Capture only the artifacts needed for diagnosis, and retain them for the shortest period that supports debugging. Visual assertions also add image comparison work, so keep baseline sets focused on pages and states that matter.
Parallel workers improve test throughput, but they increase temporary storage usage. Per-test output directories prevent collisions; CI retention policy determines how much storage remains after the run.
Or skip the browser setup
If you need a screenshot file from a URL instead of a test-run artifact or visual baseline, ScreenshotNeo provides a single GET request. 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. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing result.
See the ScreenshotNeo API documentation for the available 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 buffer = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', buffer));
ScreenshotNeo also supports full-page capture with lazy images loaded, CSS element capture, dark mode, device presets and custom viewports, retina scale, PDF output, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed links, asynchronous jobs with signed webhooks, bulk capture for 100 URLs per call, a usage API, and an OpenAPI specification. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.
There is a free plan with 1,000 screenshots per month and no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account.
FAQ
What setting changes Playwright’s failure screenshot directory?
Set outputDir in the Playwright configuration. Keep use.screenshot set to 'only-on-failure' if you only want screenshots for failed tests.
Can I use outputDir for visual regression baselines?
No. Baselines from toHaveScreenshot() are controlled by snapshotPathTemplate or the assertion’s pathTemplate.
Why does Playwright delete my screenshots?
Playwright cleans outputDir at the start of a run. Preserve files by uploading or copying them after the run.
How do I keep screenshots from parallel tests separate?
Use testInfo.outputPath() for test-created files and include {projectName} and test path tokens in snapshot templates.
Should I use snapshotDir?
New configurations should use snapshotPathTemplate; Playwright marks snapshotDir as discouraged.


