Short answer: you cannot rely on an absolute pathname passed to cy.matchImageSnapshot() to choose where a baseline image is stored. The reviewed @simonsmith/cypress-image-snapshot documentation shows relative snapshot names, including nested names, and documents e2eSpecDir for arranging snapshots alongside your Cypress spec tree. Use those supported controls instead. If you need the absolute path of a Cypress screenshot artifact, read it from Cypress metadata rather than trying to calculate it.
What “absolute path” means in this setup
Three different paths are commonly confused:
| What you are controlling | Supported control | What it affects |
|---|---|---|
| Image-snapshot baseline | Relative name passed to matchImageSnapshot and the plugin’s e2eSpecDir |
The baseline snapshot tree maintained by the image-snapshot plugin |
| Cypress screenshot artifact | cy.screenshot(name) and screenshotsFolder |
PNG/JPEG screenshots written by Cypress, not plugin baselines |
| Resolved pathname after saving | onAfterScreenshot metadata or Cypress Node events |
Reports the path Cypress actually selected; it does not redirect output |
An absolute string such as /var/tmp/baselines/home is not documented as a supported destination for matchImageSnapshot. Treating it as a guaranteed path can produce a wrong directory layout or behavior that changes between package forks and versions.
Use a relative snapshot name
Nested names are the documented approach
Pass a project-relative, slash-separated name:
cy.matchImageSnapshot('checkout/home');
The plugin README demonstrates this style with names such as some/dir/image. The name is interpreted within the plugin’s snapshot root, rather than as an operating-system pathname beginning at /.
Keep names stable
- Use a deterministic name for the page or component under test.
- Use forward slashes for nested folders so the name is portable across operating systems.
- Do not include a drive letter, leading slash,
..traversal, or a full path copied from a local machine. - Include a state or viewport suffix when the same spec captures several legitimate baselines, for example
account/dark/desktop.
describe('account page', () => {
it('matches the desktop light theme', () => {
cy.visit('/account');
cy.matchImageSnapshot('account/light/desktop');
});
it('matches the desktop dark theme', () => {
cy.visit('/account');
cy.matchImageSnapshot('account/dark/desktop');
});
});
Align the snapshot tree with your spec tree using e2eSpecDir
Why this option exists
Cypress 10 and later can remove a common ancestor from spec paths when it builds output directories. The resulting path can vary depending on which specs run together. The @simonsmith/cypress-image-snapshot README documents e2eSpecDir so the plugin can remove the configured E2E directory prefix and mirror the remaining spec structure.
#1 Best Overall
Configuration example
In the plugin setup where you register the command, pass the E2E directory that matches the directory used by your specPattern:
addMatchImageSnapshotCommand({
e2eSpecDir: 'cypress/e2e/'
});
With that setting, a relative name such as checkout/home remains a name inside the plugin’s snapshot hierarchy, while the spec-derived portion is organized consistently. The exact registration file differs between Cypress projects, so keep this option in the setup prescribed by the installed package version.
Check the installed fork before changing paths
The package name matters. The guidance above is for @simonsmith/cypress-image-snapshot. Older cypress-image-snapshot forks and other releases may expose different options or path rules. The current maintainer documentation says its tested Cypress versions are 15.x and 16.x, requires Cypress 15.10 or later for its Cypress.expose support, and recommends version 10.x with Cypress 13.x or 14.x. Verify your installed package and version in package.json, lockfile, and the package’s own README before relying on an option.
Rank #2
If you actually want to move Cypress screenshots
Change the base folder with screenshotsFolder
screenshotsFolder changes where Cypress writes screenshots created by cy.screenshot(). Its documented default is cypress/screenshots. It does not change the image-snapshot plugin’s baseline root.
Recommended Free Tools
const { defineConfig } = require('cypress');
module.exports = defineConfig({
screenshotsFolder: 'artifacts/cypress-screenshots',
e2e: {
specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}'
}
});
Use this when the requirement is “put ordinary Cypress screenshots under an artifacts directory,” not “put matchImageSnapshot baselines under an absolute pathname.” See the Cypress configuration reference for the current configuration name and default.
Use a nested relative screenshot name
cy.screenshot('checkout/failure');
Cypress combines the relative name with its screenshots folder and the spec-derived directory. A nested name creates nested folders; it is still not an absolute destination.
Rank #3
How to obtain the full path Cypress selected
Read props.path in onAfterScreenshot
If you need to log, attach, or process the actual file after Cypress saves it, use the callback metadata:
cy.screenshot('checkout/failure', {
onAfterScreenshot: (element, props) => {
// props.path is the resolved pathname on the machine running Cypress
cy.log(`Saved screenshot: ${props.path}`);
}
});
The callback reports the resolved location; it does not move the file. The Cypress screenshot API documents the callback metadata and nested naming behavior.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Use Node-side screenshot events for automation
For CI processing, use Cypress’s Node events that receive screenshot details, including the resolved path. This is preferable to reconstructing a path from the spec filename. Cypress can strip the longest common ancestor across the specs in a run, so a path you calculate locally may differ when one spec is run alone versus as part of a larger set. The path rules and resolved-path guidance are described in Writing and organizing Cypress tests.
Rank #4
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
setupNodeEvents(on) {
on('after:screenshot', (details) => {
console.log(`Cypress wrote: ${details.path}`);
return details;
});
}
}
});
Keep the event handler observational. Changing details.path after the fact does not turn it into a new output destination unless you explicitly copy or move the file yourself.
Recommended decision path
- Need a visual-regression baseline? Use
cy.matchImageSnapshot('relative/name'). Do not pass an absolute pathname as if it were a documented destination. - Need the baseline tree to follow your specs? Set
e2eSpecDirto the directory represented in your E2E configuration. - Need ordinary Cypress screenshots somewhere else? Set
screenshotsFolder, then use a relative name withcy.screenshot(). - Need to know where a file landed? Read
props.pathinonAfterScreenshotor consume the Node event details. - Using an older fork? Inspect that exact package’s README, types, and implementation before assuming any of these options exist.
Troubleshooting path problems
| Symptom | Likely cause | Fix |
|---|---|---|
| An absolute-looking name creates an unexpected folder | The plugin documents relative names, not absolute destinations | Replace it with a relative nested name and configure e2eSpecDir if spec alignment is needed. |
Baselines are not under the configured screenshotsFolder |
screenshotsFolder controls Cypress screenshots, not plugin baselines |
Leave the settings separate; use the image-snapshot plugin’s documented snapshot configuration. |
| The same spec appears in different directories in CI | Cypress removed a different longest common ancestor because the set of executed specs changed | Use e2eSpecDir for the plugin tree, and read the resolved path from metadata instead of reconstructing it. |
e2eSpecDir has no visible effect |
The value does not match the directory portion of your E2E spec pattern, or the installed fork does not support it | Compare the option with the package README and your actual specPattern; confirm the installed package and version. |
| A callback logs an empty or wrong path | The code is logging a guessed path or running before the screenshot callback | Log props.path inside onAfterScreenshot, or use the Node screenshot event after Cypress reports completion. |
| Configuration works locally but not in CI | Absolute paths differ by runner, operating system, workspace, or container | Keep names relative and portable; treat the reported path as environment-specific and publish it as a CI artifact if needed. |
Path portability and maintenance
Commit baselines by logical name
Visual baselines are easiest to review when their names describe the test state rather than a developer’s home directory. Relative names also avoid hard-coding Windows drive letters or Unix mount points into a repository.
Do not infer paths from one run
Cypress’s common-ancestor stripping means that the same spec can have a different resolved directory when the run includes a different collection of specs. For tooling that uploads or compares files, consume the path Cypress provides at runtime.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesSeparate artifact retention from baseline naming
Use screenshotsFolder and CI artifact settings for temporary screenshots. Use the image-snapshot plugin’s relative names and e2eSpecDir for committed visual-regression baselines. Keeping those concerns separate prevents a screenshot artifact setting from appearing to “break” baseline generation.
Or skip the browser setup
If your goal is simply to obtain a clean screenshot from a URL rather than maintain Cypress baselines, ScreenshotNeo makes one HTTP request. It accepts the cookie or consent banner as a visitor, removes more than 60 known consent platforms plus newsletter popups and chat widgets before capture, and bills only clean shots. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; the response identifies the result with X-Page-Verdict and X-Billed headers. It also provides an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
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)
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}`);
See the ScreenshotNeo API documentation for request options. Every plan includes its features: full-page and element capture, device and retina settings, dark mode, PDF output, custom CSS and JavaScript, waiting and blocking controls, headers, cookies, user-agent, authorization, timezone and geolocation, transparent backgrounds, resizing, caching with a chosen TTL, signed links, asynchronous jobs with webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. The parameter names used by other screenshot APIs also work for easier migration.
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; higher plans are Growth ($15/15,000), Pro ($39/60,000), Scale ($99/250,000), and Business ($249/1,000,000). Yearly billing provides two months free. Create a free ScreenshotNeo account to start without a card.
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.

