Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
A Cypress screenshot failure has two separate possible causes: your application really changed, or the capture is different even though the UI is correct. Cypress’s built-in cy.screenshot() only captures an image; a plugin or hosted visual-testing service compares that image with a baseline. Open the diff first, classify the changed pixels, then stabilize state, data, time, rendering, and environment before approving any baseline.
This guide gives a deterministic workflow, Cypress examples, failure branches, and an API alternative when browser setup is the problem.
What a Cypress screenshot failure actually means
Cypress does not provide the image-comparison layer itself. cy.screenshot() creates a PNG (or the configured image format); a plugin or service such as Cypress Image Diff, Cypress Image Snapshot, Cypress Visual Regression, Visual Regression Diff, Pixeleye, Applitools, Argos, Chromatic, or Sauce Labs Visual performs the comparison and reports a diff. See Cypress’s visual testing guide for the current integration list.
Recommended Free Tools
A diff is therefore a signal, not a diagnosis. Common explanations are:
#1 Best Overall
- An intentional layout, color, typography, or content change.
- Uncontrolled API data, dates, timers, ads, or user-specific content.
- A screenshot taken before the intended state finished rendering.
- Different browser, operating-system, viewport, font, display scale, or CI image.
- An animation, transition, lazy image, or capture-boundary difference.
Do not approve a new baseline until you can explain the changed pixels.
Fix failures in this order
- Inspect the diff. Identify whether the change is structural, typographic, color-related, an image, dynamic text, or simply a different crop. Compare the actual and expected images at the same scale.
- Decide whether the product change is intended. If a reviewed design or code change caused it, update the baseline through your comparison tool’s documented approval workflow. If not, continue with stabilization.
- Prove the page state before capture. Add an assertion for the content or state represented by the screenshot. Avoid an arbitrary sleep as your main synchronization method.
- Control data and time. Stub changing requests, freeze clocks where dates or countdowns appear, and remove random or user-dependent values.
- Eliminate transient rendering. Disable CSS transitions and animations in test mode or wait for a specific transition to finish. Do not assume Cypress actionability animation settings stop every page animation.
- Match the rendering environment. Use the same OS or container image, browser version, fonts, viewport, and display characteristics for baseline creation and comparison.
- Narrow the capture. If a full page includes unrelated dynamic regions, capture the component or element under test.
- Review and approve only then. For an intermittent mismatch, fix the nondeterministic cause; a retry can expose flakiness but does not prove that a new appearance is correct.
Make the Cypress test wait for the right state
cy.screenshot() is asynchronous. The page can change between issuing the command and the actual capture, and chained assertions are not retried by the screenshot command. Keep state assertions as separate commands immediately before the screenshot.
cy.intercept('GET', '/api/products', { fixture: 'products.json' }).as('products');
cy.visit('/catalog');
cy.wait('@products');
cy.get('[data-cy="catalog"]')
.should('be.visible')
.and('contain', 'Starter plan');
cy.screenshot('catalog-ready');
The assertion should describe the state the image depends on: a selected tab, loaded table row, expanded menu, or completed request. If the application exposes a stable readiness marker, assert that marker rather than waiting a fixed number of milliseconds.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Use fixtures for changing APIs
Live responses can change ordering, prices, inventory, feature flags, or text while the test is running. Intercept the request and return a fixture (or a deterministic inline response). Give each scenario its own data so a baseline represents a known state.
Rank #2
cy.intercept('GET', '/api/account', {
fixture: 'account/standard-user.json'
}).as('account');
cy.visit('/account');
cy.wait('@account');
cy.get('[data-cy="account-heading"]').should('have.text', 'Account');
cy.screenshot('account');
Freeze time-dependent UI
Dates, clocks, countdowns, relative-time labels, and rotating offers can change between runs. Use Cypress’s clock controls before the application schedules its timers, then assert the displayed result.
cy.clock(new Date('2026-01-15T12:00:00Z').getTime());
cy.visit('/billing');
cy.get('[data-cy="renewal-date"]').should('contain', 'Jan 15');
cy.screenshot('billing');
Keep the fixed date in the test’s contract. If the page reads server time rather than browser time, stub that response as well.
Stop animation and layout movement
Cypress documents waitForAnimations and animationDistanceThreshold for action commands such as clicks. They do not stop an unrelated CSS animation from changing a snapshot. The screenshot API separately documents disableTimersAndAnimations, enabled by default for screenshot capture, but page code, video elements, and delayed layout can still create variation. See the Cypress.Screenshot API.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsAdd a test-only stylesheet that disables transitions and animations where visual stability matters:
Rank #3
/* cypress/support/e2e.css */
*, *::before, *::after {
animation: none !important;
transition: none !important;
caret-color: transparent !important;
}
Load it in your test setup or inject equivalent rules before visiting the page. For an animation whose final state is the subject of the test, wait for its completion event or assert the final CSS/state instead of taking a mid-transition image.
Make baselines comparable
Set the viewport explicitly
Cypress’s documented default viewport is 1000 × 660 pixels. That is a default, not a universal visual-testing target. Set the dimensions your product supports in the test or configuration:
describe('dashboard visuals', () => {
beforeEach(() => {
cy.viewport(1440, 900);
});
it('matches the dashboard baseline', () => {
cy.visit('/dashboard');
cy.get('[data-cy="dashboard"]').should('be.visible');
cy.screenshot('dashboard-1440');
});
});
Keep viewport width and height, device-pixel ratio, browser zoom, and full-page versus viewport mode consistent. The available configuration options and defaults are listed in Cypress configuration.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Pin the rendering stack
- Run baseline generation and comparison in the same CI container or OS image.
- Pin the browser version where practical.
- Install the same font files; a fallback font changes wrapping and every pixel below it.
- Keep browser zoom and display scaling at known values.
- Use identical locale, timezone, and color-scheme settings when the UI responds to them.
If local runs pass but CI fails, first compare these inputs rather than raising a global difference threshold.
Rank #4
Choose the smallest useful boundary
A full-page screenshot can include a cookie banner, rotating recommendation, footer timestamp, or unrelated experiment. Cypress supports viewport, full-page, runner, and element capture modes; the comparison integration determines how masking and thresholds are configured. Capturing the component under test reduces unrelated pixels:
cy.get('[data-cy="checkout-summary"]')
.should('be.visible')
.screenshot('checkout-summary');
For unavoidable dynamic regions, prefer a narrow mask or blackout supported by your visual tool. Do not loosen the threshold for the entire page to hide one timestamp.
Understand Cypress’s automatic screenshots and retries
During cypress run, Cypress automatically saves screenshots for failed tests by default. These are diagnostic artifacts, not automatically approved visual baselines; the distinction is described in Screenshots and videos.
Retries are disabled by default. You can enable them to reveal whether a mismatch is intermittent, but a passing retry only demonstrates that output varied. Cypress lists animations, API calls, server or database availability, resource dependencies, and network issues as possible race conditions. Follow the underlying cause rather than accepting whichever retry passes; see test retries.
Common failure patterns and precise fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Only text dates or countdowns differ | Browser or server time moved | Use cy.clock() and stub server-time responses. |
| Cards reorder between runs | Uncontrolled API sorting or live data | Return a fixture with a fixed order and assert the loaded response. |
| Large vertical shift below a heading | Font fallback, late image, or layout shift | Pin fonts, wait for the image/state, and assert the final container dimensions or visibility. |
| Diff appears around a spinner or menu | Capture occurred during a transition | Disable test animations or wait for the final state marker. |
| Local passes, CI fails everywhere | Different browser, OS, font, viewport, or scaling | Use one pinned image and explicit viewport; compare environment metadata. |
| Only an ad, chat bubble, or consent panel differs | Third-party content | Block or stub the request, hide the selector in test mode, or narrowly mask it. |
| Full page fails but component passes | Unrelated page regions are unstable | Capture the relevant element or define a deliberate mask. |
| Failure is occasional and retry passes | Race condition or nondeterministic data | Instrument the state, network, and timing; do not approve the retry’s image automatically. |
When to approve a new baseline
Approve only after code review confirms an intentional change and the diff contains no unexplained pixels. Record the reason with the baseline update so a future reviewer can distinguish a design revision from a temporary workaround. If a diff is caused by an uncontrollable third-party region, isolate that region with a supported mask or deterministic test substitute. A blanket threshold increase trades away detection across the entire image.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Choosing local comparison or a hosted service
Cypress’s guide describes two broad models:
| Consideration | Local/open-source plugin | Hosted commercial service |
|---|---|---|
| Comparison | Usually local pixel-by-pixel comparison | Service-managed comparison workflow varies by provider |
| Baselines | Files owned and updated by the team | Provider-managed storage and approval workflow |
| Review | CI artifacts and repository review | Dashboard and, for some services, pull-request review |
| Rendering | You maintain matching environments | Provider may manage render infrastructure |
| Cost and data | Guide characterizes open-source plugins as free with images kept in team infrastructure | Paid subscription category; verify current terms and data handling |
Choose based on baseline ownership, browser and viewport coverage, environment consistency, review flow, data handling, price, and how much infrastructure your team wants to maintain. Cypress itself does not compare the images.
Or skip the browser setup
If your immediate need is a deterministic screenshot rather than an in-browser Cypress interaction, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one request. It is a capture API, so you still need your chosen comparison tool for baseline diffs, but it removes browser automation setup and exposes the capture result through response headers.
Free tools Windows power users keep installed
One-click scans. No signup required.
For example, the cURL request below captures Stripe as WebP (the API infers the output from the filename):
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 fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
See the ScreenshotNeo documentation for parameters and response details. Before capture it accepts the cookie or consent banner as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. 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. It also offers an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. You can sign up for the free plan.
Final diagnostic checklist
- Did you inspect the actual diff and classify the changed pixels?
- Is the application change intentional and reviewed?
- Does the test assert the target state immediately before capture?
- Are API responses, clock, locale, timezone, and random values deterministic?
- Are transitions, lazy images, and third-party widgets settled or isolated?
- Do baseline and comparison use the same OS image, browser, fonts, viewport, and scaling?
- Is the screenshot boundary limited to the behavior under test?
- Was a new baseline approved only after the visual reason was understood?
Frequently Asked Questions
Does Cypress compare screenshots by itself?
No. Cypress captures images with commands such as cy.screenshot(); a plugin or hosted visual-testing service performs baseline comparison and review.
Why does a screenshot still vary when Cypress disables animations?
The screenshot setting addresses capture-time timers and animations, but server data, CSS or JavaScript driven page motion, fonts, late resources, and environment differences can still change pixels.
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 & 11Should I increase the visual diff threshold to stop failures?
Only when your comparison tool’s documented, narrowly scoped tolerance matches an understood rendering variation. A global increase can hide real regressions; stabilize the cause or mask only the dynamic region first.
Are Cypress failure screenshots suitable as visual baselines?
They are diagnostic screenshots Cypress saves for failed runs. They are not automatically baseline comparisons or approvals.
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.

