How to Set the Playwright Screenshot Directory
Configure Playwright screenshot folders correctly for test artifacts, explicit captures, and visual baselines with runnable examples and fixes.

Direct answer: Set Playwright Test’s top-level outputDir to choose where test-run artifacts are written. Configure automatic capture separately with use.screenshot.
import { defineConfig } from '@playwright/test';
export default defineConfig({
outputDir: './screenshots',
use: {
screenshot: 'only-on-failure',
},
});
With this configuration, Playwright writes test artifacts below ./screenshots. The documented default is test-results, resolved under the package configuration directory. Playwright cleans the configured output directory at the start of a run and creates a unique subdirectory for each test, which keeps parallel tests from writing to the same location.
That setting applies to artifacts generated by Playwright Test. It does not change the destination of every screenshot API. An explicit page.screenshot() call receives its own path, while visual assertion baselines created by expect(page).toHaveScreenshot() use snapshotPathTemplate.
Choose the screenshot destination by purpose
| What you are saving | Setting or API | Typical location behavior |
|---|---|---|
| Automatic screenshots, traces, and videos from a test run | outputDir plus use.screenshot |
Inside the configured output directory, separated by test |
| A screenshot explicitly requested in test code | page.screenshot({ path }) |
The path you provide; a relative path uses the current working directory |
| Expected images for visual assertions | snapshotPathTemplate |
A snapshot directory determined by the template and test metadata |
Before changing a path, identify which of these three outputs you mean. Changing outputDir will not relocate visual baselines, and changing a page.screenshot() path will not change automatic failure artifacts.

Configure automatic Playwright Test screenshots
Set one directory for all projects
Put outputDir at the top level of playwright.config.ts when every project should use the same artifact root.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
outputDir: './artifacts/playwright',
use: {
screenshot: 'on',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
],
});
use.screenshot accepts 'off', 'on', or 'only-on-failure'. Use 'on' when every test needs an image, 'only-on-failure' when artifacts are mainly for diagnosis, and 'off' when screenshots are unnecessary. The capture policy controls whether Playwright takes screenshots; it does not name a destination directory.
Give one project a different output directory
A project-level outputDir overrides the top-level value for that project. This is useful when browser engines, locales, or deployment targets need separate artifact trees.
import { defineConfig } from '@playwright/test';
export default defineConfig({
outputDir: './artifacts/all',
projects: [
{
name: 'desktop',
outputDir: './artifacts/desktop',
use: { screenshot: 'only-on-failure' },
},
{
name: 'mobile',
outputDir: './artifacts/mobile',
use: { screenshot: 'only-on-failure' },
},
],
});
Keep project directories distinct if your CI collects files by project. Playwright still creates per-test subdirectories below each configured root.
Understand cleanup and CI retention
Playwright cleans the configured output directory at the start of a run. Do not use outputDir as a permanent archive for screenshots that must survive later runs. Copy artifacts to a retained CI artifact store after the test process finishes, or write long-lived exports to a separate directory from your test output directory.
The cleanup behavior also means a path outside the Playwright output tree is safer for manually curated reference material. Keep generated diagnostics and committed assets separate so a test run cannot remove them.
Save an explicit screenshot from test code
When a test decides exactly when to capture, call page.screenshot() with a path.
import { test } from '@playwright/test';
test('save a checkout screenshot', async ({ page }) => {
await page.goto('https://example.com');
await page.screenshot({ path: 'artifacts/manual/checkout.png', fullPage: true });
});
A relative path passed directly to the screenshot API is resolved from the current working directory, not automatically from the test file or outputDir. That can surprise developers who run tests from different directories in local shells and CI.
Use testInfo.outputPath() for test-scoped files
For a screenshot that belongs with one test’s other results, use the test’s testInfo object. The helper returns a path inside that test’s output directory and supports path segments.
import { test } from '@playwright/test';
test('capture the account page', async ({ page }, testInfo) => {
await page.goto('https://example.com/account');
const path = testInfo.outputPath('screenshots', 'account.png');
await page.screenshot({ path, fullPage: true });
});
This helper keeps parallel tests from interfering with one another and constrains the resulting path to the test output directory. Use it when you want predictable, test-specific placement without manually inventing unique filenames.
Put visual comparison baselines in their own directory
expect(page).toHaveScreenshot() compares the current image with an expected baseline. Baselines are controlled by snapshotPathTemplate, not by outputDir.
import { defineConfig } from '@playwright/test';
export default defineConfig({
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
});
The template can use tokens including {testDir}, {testFilePath}, {projectName}, {arg}, and {ext}. Relative template paths resolve relative to the configuration directory.
import { test, expect } from '@playwright/test';
test('homepage visual baseline', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveScreenshot('homepage.png');
});
The path argument is constrained to the snapshot directory associated with the test file. Supplying a path that escapes that directory can throw an error. Treat baselines as versioned test inputs, while treating outputDir files as disposable run artifacts.
Path resolution, naming, and edge cases
- Configuration-relative paths: A relative
outputDirand a relativesnapshotPathTemplateare interpreted from the Playwright configuration directory. - Current-working-directory paths: A relative
pathpassed topage.screenshot()follows the process working directory. - Parallel workers: Prefer
testInfo.outputPath()or unique names when writing explicit files. Avoid a fixed filename shared by tests. - Multiple projects: Use a project-level
outputDirwhen artifacts must be separated by browser, device, or environment. - Full-page images:
fullPage: truechanges the captured image dimensions, not the directory. - Missing parent folders: Let Playwright create the destination when using its output helpers, or create custom parent directories before using a separately managed path.
- Git repositories: Ignore generated output directories such as
test-resultsorartifacts; commit visual baselines only when they are intentionally part of the test suite.
Common errors and fixes
| Symptom | Cause | Fix |
|---|---|---|
Changing outputDir does not move toHaveScreenshot() images |
Baselines use snapshot configuration | Set snapshotPathTemplate and regenerate baselines deliberately |
| Explicit screenshots appear in an unexpected folder | Relative screenshot paths use the current working directory | Use an absolute path, a known project root, or testInfo.outputPath() |
| Files disappear before a run | Playwright cleans outputDir at run start |
Archive artifacts elsewhere after the run; do not store permanent files there |
| Parallel tests overwrite one another | Tests share a fixed explicit filename | Use testInfo.outputPath() or include test metadata in the name |
| Only failed tests have images | use.screenshot is set to 'only-on-failure' |
Change it to 'on' when every test needs a screenshot |
| A project still writes to the old directory | A project-level value overrides the top-level value | Inspect each project object and remove or update its override |
| A snapshot path is rejected | The path escapes the permitted snapshot directory | Use a name or path segment inside the configured snapshot tree |
Performance, reliability, and cost considerations
Capturing every test screenshot increases browser work and artifact volume. For large suites, 'only-on-failure' limits routine output while preserving failure evidence. Explicit screenshots should be taken after the page reaches the state you intend to document; otherwise timing differences can create noisy artifacts or visual diffs.
Separate disposable artifacts from committed baselines. A stable snapshotPathTemplate, deterministic test data, and consistent browser project settings make visual comparisons easier to review. In CI, retain the output directory only for the duration needed to inspect failures, then upload it to the CI system’s artifact storage.
Playwright itself does not charge per screenshot. Your costs come from browser execution, CI minutes, storage, and any external screenshot service you add. A hosted API can remove browser installation and maintenance work, but it introduces request limits, authentication, and network-failure handling.
Or skip the browser setup
If you need website images rather than Playwright test artifacts, ScreenshotNeo provides a single GET request that returns PNG, JPEG, WebP, or PDF. Its API accepts full-page capture, element selectors, dark mode, device and viewport settings, custom CSS and JavaScript, waits, blocking rules, headers, cookies, user agents, authorization, timezone, geolocation, resizing, caching, signed links, asynchronous jobs, bulk capture, and more. See the ScreenshotNeo documentation for request parameters.
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(`Screenshot request failed: ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
await Bun.write('shot.webp', data);
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers identify the page verdict and whether the request was billed. An MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
What is Playwright’s default screenshot directory?
Playwright Test’s documented default output directory is test-results under the package configuration directory.

Can I set outputDir inside use?
No. Set outputDir at the top level or on an individual project. Put screenshot inside use.
Does outputDir affect screenshots made with page.screenshot()?
Only when you explicitly pass a path derived from the test output directory, such as testInfo.outputPath(). A direct relative path follows the current working directory.
Where should visual baselines go?
Use snapshotPathTemplate and keep the resulting snapshot directory under version control when the baselines are part of your test suite.
How do I keep artifacts from being deleted?
Copy or upload them after the Playwright run. The configured output directory is cleaned at the beginning of the next run.


