Playwright has three different “screenshot folder” settings, and choosing the wrong one is the usual source of confusion. Set outputDir for automatic test artifacts such as failure screenshots, videos, and traces; use testInfo.outputPath() for a screenshot your test code writes; and use snapshotPathTemplate (or an assertion-specific pathTemplate) for toHaveScreenshot() baselines.
Choose the setting that owns your screenshot
Before changing a path, identify what creates the file. Playwright Test treats these outputs separately:
| What you are saving | Correct setting or API | What it controls |
|---|---|---|
| Automatic failure screenshots, videos, and traces | outputDir in playwright.config.ts |
The run’s artifact directory |
A file created by page.screenshot() in test code |
testInfo.outputPath() or testInfo.outputDir |
A path inside the current test’s output directory |
Expected images for expect(page).toHaveScreenshot() |
snapshotPathTemplate, or expect.toHaveScreenshot.pathTemplate |
The baseline (snapshot) layout |
These settings are not interchangeable. Changing outputDir does not relocate visual-comparison baselines, and changing a snapshot template does not move failure artifacts. The Playwright TestConfig API documents the run directory and snapshot options.
Move automatic screenshots and other test artifacts with outputDir
Put outputDir at the top level of your Playwright configuration. This example stores run artifacts under an artifacts directory and captures screenshots only when a test fails:
#1 Best Overall
import { defineConfig } from '@playwright/test';
export default defineConfig({
outputDir: './artifacts',
use: {
screenshot: 'only-on-failure',
},
});
The documented default is <package.json-directory>/test-results. A relative value such as ./artifacts is resolved from the configuration directory. The use.screenshot option accepts 'off', 'on', or 'only-on-failure'; it determines whether Playwright automatically captures screenshots. Video and trace files also go to the test output directory when those features are enabled. See the configuration options for the capture controls.
What happens at the start of a run
Playwright cleans outputDir at the start of a run and creates a unique subdirectory for each test. This prevents parallel tests from writing to the same test folder, but it also means files from an earlier run are not a durable archive. The API documentation states: “The output directory is cleaned at the start.” Configure your CI system to publish the directory as an artifact after the run if you need to retain it.
Keep artifacts in a predictable CI location
Use a repository-relative directory when your CI job collects files by path. For example, outputDir: './artifacts/playwright' keeps screenshots, videos, and traces below one known root. Do not place long-lived reports inside this directory unless your pipeline copies them elsewhere, because the next Playwright run removes the output directory first.
Save an explicit page.screenshot() inside the test’s folder
When the screenshot is created by your own test code, generate the filename with the test-scoped testInfo.outputPath() helper. It keeps the file inside that test’s unique output directory:
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'),
});
});
The resulting path is nested below the current test’s output directory, alongside other artifacts from that test. You can create further subdirectories, such as screenshots/page.png, to separate several captures. The resolved path must remain inside the current test output directory; this is an intentional safety boundary documented in the TestInfo API.
When to use testInfo.outputDir
testInfo.outputDir gives you the directory itself when a library needs a folder rather than one filename. Prefer testInfo.outputPath('name.ext') for individual files because it handles path construction and maintains the same per-test location rule. Do not hard-code a shared folder such as ./screenshots from parallel tests: names can collide and the files will no longer be associated with the test that produced them.
Rank #2
Move toHaveScreenshot() baselines with snapshotPathTemplate
Visual comparison images are snapshots, not run artifacts. Set a template in the top-level configuration when you want to control their layout:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
});
This template places expected images below __screenshots__ within the test directory. A relative template is resolved relative to configDir. The available tokens shown in the documentation include {testDir}, {testFilePath}, {projectName}, {arg}, and {ext}. {arg} represents the snapshot argument (for example, a name passed to toHaveScreenshot()), while {ext} is the generated image extension.
Free tools Windows power users keep installed
One-click scans. No signup required.
The visual comparisons guide shows the same template approach for screenshot assertions. The TestConfig API describes snapshotPathTemplate as the template that controls locations for supported snapshot assertions, including screenshot, ARIA, and generic snapshots.
Use a custom folder for screenshot assertions only
If you want other snapshot kinds to keep their normal paths, configure the screenshot assertion specifically:
import { defineConfig } from '@playwright/test';
export default defineConfig({
expect: {
toHaveScreenshot: {
pathTemplate:
'{testDir}/__screenshots__{/projectName}/{testFilePath}/{arg}{ext}',
},
},
});
The optional slash form {/projectName} adds a directory separator only when {projectName} has a value. Including the project name is useful when multiple projects (for example, different browsers or device profiles) should have separate baseline trees.
Do not start a new configuration with snapshotDir
snapshotDir is discouraged for path configuration. Playwright points users to snapshotPathTemplate instead, which gives you token-based control over the complete path. Existing projects may still contain snapshotDir; migrate deliberately and verify the generated paths before deleting old baselines.
Find the exact path Playwright expects
Use the TestInfo helpers when tooling, diagnostics, or a reporter needs to know where a file belongs:
testInfo.outputPath('file.png')resolves an arbitrary file in the current test’s output directory.testInfo.snapshotPath()resolves the expected snapshot location using your configured template.- The
kindoption ontestInfo.snapshotPath()selects screenshot, ARIA, or generic snapshot templates. The API reference markskindas added in Playwright v1.53, so check the installed version before using it.
Print the resolved value temporarily when diagnosing a layout:
test('show paths', async ({ page }, testInfo) => {
console.log('output:', testInfo.outputPath('debug/page.png'));
console.log('snapshot:', testInfo.snapshotPath());
await page.goto('https://example.com');
});
Path-template details can change between Playwright releases. Check the API reference for the version installed in your project, especially when upgrading a configuration created before snapshotPathTemplate (documented since v1.28).
Common configuration failures and fixes
“My failure screenshot is still in test-results.”
Check that outputDir is at the top level of the exported configuration, not nested inside use. Then confirm the test is actually failing and use.screenshot is not 'off'. Remember that a custom snapshot template cannot move failure screenshots.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →“My manually saved image ignores outputDir.”
A hard-coded path: './screenshots/a.png' is controlled entirely by your code. Replace it with testInfo.outputPath('screenshots/a.png') to place the file in the test’s output directory and to avoid collisions between parallel tests.
“The folder is empty after I rerun tests.”
That is expected when the folder is outputDir: Playwright cleans it at run start. Publish or copy artifacts after each run, or store durable reports outside the Playwright output directory.
Rank #4
“toHaveScreenshot() still writes to the old snapshots folder.”
Confirm that the template is top-level (snapshotPathTemplate) or under expect.toHaveScreenshot.pathTemplate. Check for another config file being selected by your test command, and inspect testInfo.snapshotPath() to see the resolved path. A project name token can also add a directory you did not see in a single-project run.
“Playwright rejects my explicit screenshot path.”
The path returned by testInfo.outputPath() must stay inside the current test’s output directory. Remove parent-directory segments and absolute paths that escape that directory. If you need a final export elsewhere, write to the test output first and copy it after the test process completes.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
“Baselines collide across projects.”
Add {projectName} (or the conditional {/projectName} form) to the screenshot template. This separates projects by name while retaining the same test-file and argument structure.
Practical organization patterns
One folder for CI artifacts, repository folders for baselines
A common arrangement is outputDir: './artifacts/playwright' for transient run files and snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}' for version-controlled visual baselines. The two trees then have different lifecycles: CI artifacts are cleaned and uploaded, while baselines change only when you intentionally update them.
Separate browser projects
When projects represent Chromium, Firefox, WebKit, or device profiles, include {projectName} in the assertion template. This prevents one project’s expected image from replacing another’s and makes review of browser-specific changes easier.
Use stable names for several captures in one test
Give each explicit capture a distinct relative name, such as testInfo.outputPath('screenshots/header.png') and testInfo.outputPath('screenshots/footer.png'). For assertions, pass distinct names to toHaveScreenshot() so {arg} produces separate baseline files.
Recommended Free Tools
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server when you need an image of a URL rather than a Playwright test artifact. It accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; those steps can be turned off. 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. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
Use the API documentation at screenshotneo.com/docs/ for the full option list. A one-call cURL capture is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The equivalent Python request is:
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)
And in 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}`);
Every plan includes the same feature set, including full-page and element captures, device and retina controls, PDF output, custom CSS and JavaScript, request blocking, cookies and headers, geolocation, caching, signed links, asynchronous webhooks, bulk capture, and a usage API. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for the free plan to try it without a card.
Final decision checklist
- Use
outputDirwhen Playwright itself creates failure screenshots, videos, or traces. - Use
testInfo.outputPath()for files created bypage.screenshot()or other test code. - Use
snapshotPathTemplatefor all snapshot kinds, orexpect.toHaveScreenshot.pathTemplatefor screenshot baselines only. - Include
{projectName}when projects need isolated baseline trees. - Expect
outputDirto be cleaned at the start of every run, and upload anything you need to keep.
Frequently Asked Questions
Can I use one setting to move every Playwright image?
No. Automatic artifacts, explicit test screenshots, and visual-comparison baselines are produced by different subsystems and have separate path controls.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Which path should be committed to Git?
Usually the visual baselines controlled by a snapshot template. Treat the run output directory as disposable unless your team deliberately archives it.
Why does a template contain both {arg} and {ext}?
The argument distinguishes named snapshots, while the extension keeps the generated image type in the filename.
Quick Recap
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.

