How to Save Cypress Screenshots to a Custom Folder
Set Cypress’s screenshotsFolder to change the project-wide destination, or pass a nested path to cy.screenshot() for one capture.
Set screenshotsFolder in your Cypress configuration to change the common destination for screenshots. For a one-off nested path or filename, pass a relative path to cy.screenshot(). Cypress uses the configured folder for manual screenshots and failure screenshots from cypress run.
The default folder is cypress/screenshots. If you want to keep screenshots between run-mode executions, also set trashAssetsBeforeRuns: false, because Cypress clears configured asset folders before a run by default. See the official Cypress configuration reference.
1. Change the screenshots folder for the project
In a JavaScript project, edit cypress.config.js at the project root:
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
screenshotsFolder: 'artifacts/screenshots',
},
})
For a TypeScript configuration file, use the corresponding ES module form:
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
screenshotsFolder: 'artifacts/screenshots',
},
})
The path is relative to the project unless you configure an absolute path. The configured folder is the root for both calls to cy.screenshot() and screenshots Cypress takes automatically for failed tests in cypress run. Confirm the supported configuration for your pinned Cypress release in the configuration reference.
2. Organize one screenshot in a nested path
If the project-wide folder is right and only one capture needs a descriptive name or subfolder, pass a relative path to cy.screenshot():
describe('login', () => {
it('shows the login form', () => {
cy.visit('/login')
cy.screenshot('actions/login/login-form')
})
})
Cypress interprets that name beneath the screenshots folder and the adjusted spec-file path, and creates nested folders as needed. A named screenshot generally follows this pattern:
{screenshotsFolder}/{adjustedSpecPath}/{name}.png
For example, with screenshotsFolder: 'artifacts/screenshots', a spec at cypress/e2e/auth.cy.js, and the name actions/login/login-form, the output is under the configured root and the adjusted spec path, followed by actions/login/login-form.png. Cypress’s screenshot command documentation describes path behavior and duplicate naming.
Duplicate names are numbered by default. To intentionally replace an existing file with the same name, pass overwrite: true:
cy.screenshot('actions/login/login-form', { overwrite: true })
Use the command path for per-capture organization; use screenshotsFolder when the common root should change for the project.
3. Override the folder for one CLI run
To try another destination without changing the checked-in configuration, pass a configuration override to cypress run:
npx cypress run --config screenshotsFolder=artifacts/ci-screenshots
The CLI override takes precedence over the value in the configuration file. To select a different Cypress configuration file, use --config-file:
npx cypress run --config-file tests/cypress.config.js
Use the CLI override for a temporary local or CI run. Use the configuration file for a destination that should be the project default. The configuration reference documents configuration and CLI overrides.
4. Keep screenshots between runs
By default, cypress run clears the contents of configured asset folders before it starts. This includes the screenshots folder and its nested files. If screenshots in that destination must persist, disable cleanup:
const { defineConfig } = require('cypress')
module.exports = defineConfig({
trashAssetsBeforeRuns: false,
e2e: {
screenshotsFolder: 'artifacts/screenshots',
},
})
trashAssetsBeforeRuns also affects the downloads and videos asset folders. If you disable cleanup, plan how old artifacts will be removed so repeated runs do not accumulate files indefinitely. See Cypress’s screenshots and videos guide.
5. Know when Cypress creates screenshots
- Manual screenshots:
cy.screenshot()works in bothcypress runandcypress open. - Automatic failure screenshots: Cypress captures these in
cypress run. - Open mode: Cypress does not automatically capture screenshots for failures in
cypress open; add a manualcy.screenshot()call if you need one.
This distinction matters when a custom folder appears empty after a test failure in the interactive runner. Refer to the Cypress screenshots and videos guide for the documented run-mode behavior.
6. Choose the right configuration
| Need | Use | Scope |
|---|---|---|
| Move all screenshots to a different root | screenshotsFolder in Cypress config |
Project runs using that config |
| Use a temporary destination in CI or locally | --config screenshotsFolder=... |
One CLI invocation |
| Place one screenshot in a subfolder or give it a specific name | cy.screenshot('relative/path') |
That command, beneath the spec path |
| Replace a same-name capture | { overwrite: true } |
That screenshot command |
| Preserve existing files before a run | trashAssetsBeforeRuns: false |
Configured asset folders |
7. Troubleshooting
The screenshot still goes to cypress/screenshots
Check that screenshotsFolder is inside the correct Cypress configuration and, for the TypeScript form, that the config is exported. If you passed a CLI override, check its spelling and value. A CLI value overrides the config file, so remove or update an old command-line setting if it points elsewhere.
The file is nested more deeply than expected
A cy.screenshot() name is interpreted beneath the screenshots root and the adjusted spec-file path. Include that structure when locating the file. The command argument is not an unrestricted absolute destination; use screenshotsFolder to change the root and a relative command path to organize a capture underneath it.
Screenshots from the previous run disappeared
Cypress clears asset folders before cypress run by default. Set trashAssetsBeforeRuns: false when the previous files need to remain, and arrange separate cleanup if the output should not grow forever.
No automatic failure screenshot appears in the interactive runner
Automatic failure screenshots are a cypress run behavior, not an automatic cypress open behavior. Add a manual cy.screenshot() call for captures you need in open mode.
A duplicate screenshot has a suffix or an old image remains
Cypress numbers duplicate names by default. Choose a unique screenshot name, or use { overwrite: true } when replacement is intentional.
The output differs on an older pinned Cypress version
Configuration behavior can depend on the Cypress version in the project. Check the documentation for the version you have pinned and confirm the active config file and CLI arguments used by the run.
8. CI, reliability, and storage notes
- Use a predictable path: a project-relative folder such as
artifacts/screenshotsis straightforward to reference from a CI job. - Account for cleanup: if a later CI step collects screenshots, ensure the run has not cleared files that step expects to preserve.
- Keep run artifacts distinct when needed: include a run or build identifier in the destination or naming scheme if parallel jobs could otherwise write to the same files.
- Manage retention: preserving assets across runs can consume storage over time; remove old captures according to your project’s artifact-retention needs.
Cypress documents folder configuration and cleanup behavior, but storage capacity and retention limits depend on the CI provider and artifact system you use.
Or skip the browser setup
If your goal is to capture a public webpage rather than save Cypress test-run screenshots, ScreenshotNeo provides a website screenshot API and MCP server. Its API accepts a URL and returns an image or PDF. Start with this cURL request; see the ScreenshotNeo API documentation for options.
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,
)
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 request failed: ${res.status}`)
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())))
- Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
- Bot checks, blank pages, failed loads, timeouts, and cache hits are never billed; response headers report the page verdict and billing status.
- An MCP server gives AI agents tools to take screenshots, inspect page information, and capture PDFs.
- 1,000 screenshots per month are free with no card. Paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account to start with 1,000 screenshots a month at no charge and no card.
FAQ
Does changing screenshotsFolder affect Cypress videos too?
No. It configures the screenshot destination. Video output has its own configuration; trashAssetsBeforeRuns is the shared cleanup setting that also affects videos and downloads.
Can I use an absolute path for cy.screenshot()?
The command name is documented as a path beneath the configured screenshot folder and adjusted spec path. Set the folder root with screenshotsFolder instead of treating the command name as an arbitrary absolute destination.
Will Cypress create the nested folders for a named screenshot?
Yes. Cypress creates the subfolder structure for the relative path passed to cy.screenshot().


