ScreenshotNeo

BlogHow-to

How to Compare Cypress Screenshots in Visual Regression Tests

Cypress captures screenshots but does not compare them with baselines. Add a visual testing tool, stabilize the browser state, and review changes before approving new baselines.

By the ScreenshotNeo team4 October 20269 min read

Cypress can capture a screenshot with cy.screenshot(), but it does not compare that image with an approved baseline. To run visual regression tests, add a Cypress-compatible plugin or hosted visual testing service. The workflow is: drive the app into a known state, capture the page or an element, compare it with an approved baseline, then review and approve intentional changes.

This guide shows how to set up a local pixel-diff workflow with Cypress Image Diff. Plugin APIs and compatibility can change, so verify its current setup against the project’s README and Cypress version before adopting it. Cypress maintains a list of other options and describes their broad trade-offs in its visual testing guide.

1. Install a comparison tool

A comparison tool supplies what Cypress’s screenshot command does not: baseline storage, image comparison, and a result that can fail when the image changes. The example below uses Cypress Image Diff, whose command is cy.compareSnapshot(name).

npm install --save-dev cypress-image-diff

Configure the plugin in cypress.config.js as shown in the plugin’s current README. The package’s setup has changed over time; use the exact configuration documented for the version you install rather than copying a setup for another plugin. Add its comparison command to Cypress support code as documented by that version. Once installed, the test example below calls the plugin command.

For a TypeScript project, use the plugin’s documented TypeScript setup and declare or import its custom Cypress command types as required. A missing command type is a TypeScript/editor issue; a runtime “command does not exist” error usually means the support file did not load the plugin command.

2. Write a test around a stable visual state

A useful visual test checks a deliberate state, not an arbitrary moment in a user journey. Visit the page, control the data it displays, perform the action, wait for the resulting state, assert it, and only then compare the image.

// cypress/e2e/todo-visual.cy.js
describe('todo visual regression', () => {
  beforeEach(() => {
    cy.viewport(1280, 800)

    // Use deterministic data so the page does not depend on a live API.
    cy.intercept('GET', '/api/items', {
      statusCode: 200,
      body: [{ id: 1, title: 'write tests', completed: false }],
    }).as('getItems')

    cy.visit('/todos')
    cy.wait('@getItems')
  })

  it('shows a completed todo consistently', () => {
    cy.contains('.todo-list li', 'write tests')
      .find('.toggle')
      .check()

    cy.contains('.todo-list li', 'write tests')
      .should('have.class', 'completed')

    // First run establishes a baseline; later runs compare against it.
    cy.compareSnapshot('completed-todo')
  })
})

The selector, route, and response shape need to match your application. If the UI loads from another endpoint, intercept that request instead. The assertion before the snapshot is important: it ensures the capture follows the intended state transition rather than racing the render.

3. Create and review the baseline

  1. Run the test in the environment you intend to use for comparisons.
  2. Inspect the initial capture. Confirm its viewport, loaded content, fonts, and state are correct.
  3. Save or approve that capture as the baseline using the plugin’s documented workflow.
  4. Run the test again. The current image should compare with the approved baseline.
  5. When a diff appears, inspect it. Fix unintended regressions; update the baseline only when the UI change is intentional and reviewed.

The plugin controls the exact baseline directory, diff artifacts, and update command. Keep generated baseline images under version control if the tool’s workflow expects that, and submit intentional baseline changes in the same review as the code change. Do not automatically bless every changed image in continuous integration: that removes the test’s ability to flag regressions.

4. Make comparisons reproducible

Fix the viewport and rendering environment

Use the same viewport dimensions, browser, operating system, display scale, and installed fonts when generating and comparing local pixel baselines. Cypress specifically recommends generating and comparing screenshots in the same environment with a fixed viewport. A local baseline made on a laptop may differ from a CI image because font rasterization or browser versions differ.

Set a project default in cypress.config.js or set it explicitly in the test with cy.viewport(width, height). The example uses 1280 × 800; choose dimensions representative of the UI you need to protect. Test a second viewport as a separate checkpoint when responsive behavior matters.

Control network data and time

Stub responses with cy.intercept() and fixtures so dates, names, counts, and ordering do not drift between runs. Wait for the aliased request and assert that the expected content is present before capturing. For time-dependent UI, freeze the browser clock before the app reads the current time:

const fixedTime = new Date(2026, 1, 1)
cy.clock(fixedTime)
cy.visit('/dashboard')

Set the clock before visiting when application code reads time during startup. If the app’s output depends on a server timestamp, stub that value as well.

Keep animation and dynamic regions from adding noise

Wait for transitions and asynchronous rendering to settle. Cypress’s waitForAnimations setting governs actionability for commands such as clicking; it does not guarantee that every page animation has stopped before a screenshot. Cypress’s built-in cy.screenshot() supports disableTimersAndAnimations, but a plugin may take its own screenshot and expose different options. Check the plugin’s screenshot configuration.

For a local screenshot taken directly with Cypress, options include capture (viewport, fullPage, or runner), blackout selectors, clip, padding for element captures, scale, timeout, and before/after screenshot callbacks. These are Cypress screenshot options; pass them through a plugin only if that plugin documents support for them. Blacking out a region hides it in the capture; some visual tools instead support masks that exclude a region from comparison. Confirm which behavior your chosen tool implements.

Prefer deterministic data over masking. If a timestamp or third-party widget cannot be controlled, mask only its small, genuinely volatile region when your comparison tool supports it. A broad threshold can conceal a real layout regression.

5. Choose the right snapshot scope and tool

Approach Useful when Trade-off
Local open-source plugin You want local or CI image comparison and control over image storage. Your team maintains baselines, rendering consistency, and diff review.
Hosted visual testing service You want managed baselines, review workflows, or broader browser and viewport coverage. Check current compatibility, retention, privacy, pricing, and the service’s supported review workflow.
Element or component snapshot You want a focused diff that points to one component or state. It will not catch unrelated page-level layout changes outside the selected element.
Full-page snapshot You need to catch page-wide layout regressions. More unrelated content can change and require review.

Every checkpoint creates review work. Cover important shared components and user-visible states rather than snapshotting every incidental step. Use full-page images for layout coverage and focused element images when a smaller failure surface makes diagnosis easier.

Cypress’s guide lists maintained open-source options such as Cypress Image Diff, Cypress Image Snapshot, Cypress Visual Regression, and Visual Regression Diff, and describes hosted integrations including Applitools Eyes, Argos, Chromatic, Happo, Percy, LambdaTest SmartUI, and Sauce Labs Visual. These tools differ in comparison, baseline, rendering, and review workflows; those category descriptions are not guarantees for every product. Check each tool’s own current documentation before selecting one. For example, Chromatic’s Cypress setup documentation describes its archive upload, cloud snapshots, pixel diff, and review process.

6. Troubleshoot common failures

Symptom Likely cause What to do
cy.compareSnapshot is not a function The plugin command was not registered, or the Cypress support file is not loaded. Check the installed package’s command setup and the supportFile configuration. Restart Cypress after configuration changes.
Every run shows a diff, including unchanged code The baseline and current run use different browsers, fonts, operating systems, viewport sizes, or scaling. Generate and compare baselines in the same pinned environment. Set an explicit viewport and inspect the raw images before changing thresholds.
Only text or dates change Live data, the current time, locale, or timezone differs. Stub responses, freeze the browser clock, and fix locale/timezone inputs where the app allows it.
Capture contains a spinner, skeleton, or partial page The snapshot runs before the application finishes rendering. Wait for the relevant request and assert the final content before the snapshot. Avoid fixed sleeps when a state assertion is available.
Diff moves between runs Animation, transitions, random content, rotating carousels, or third-party widgets are changing. Disable or wait out motion, seed randomness where possible, and mask only an uncontrollable small region if supported.
Intercept alias times out The route matcher does not match the actual request, or the browser served a cached response without a network request. Inspect the request URL and method, correct the matcher, and check browser caching. Cypress documents that cached responses do not reach cy.intercept().
Screenshot has the wrong dimensions The test inherited a different viewport, used a full-page capture, or the plugin applied its own sizing. Set the viewport explicitly and review the plugin’s capture settings. Cypress’s own screenshot capture option distinguishes viewport, full-page, and runner captures.
CI reports missing baseline images Baselines were not committed or the CI job is using a different snapshot path. Follow the plugin’s baseline update workflow, commit reviewed baselines, and keep path configuration consistent across local and CI runs.
Plugin install or command fails after a Cypress upgrade The plugin may not support the installed Cypress version. Check its current compatibility notes and release history. Upgrade, choose a maintained compatible option, or use a service with a supported integration.

7. Performance, reliability, and cost

Each visual checkpoint adds browser capture, image comparison, and artifact work. Large full-page images and many viewport variants increase runtime and the number of diffs people must review. Start with high-value states, keep images focused where appropriate, and expand coverage where a visual failure would be useful to catch.

Local pixel comparison can be free in software licensing terms, but it still uses CI time and requires the team to maintain the environment, baselines, and review process. Hosted tools generally charge for a subscription and may manage rendering and review infrastructure. Pricing, retention, and included browser coverage change; verify them with each provider. The dossier contains no reliable common benchmark or cost estimate, so compare tools against your own requirements rather than assuming a universal performance winner.

For reliability, treat the baseline as reviewed test data. Keep intentional image changes reviewable, avoid automatic baseline replacement on normal CI runs, and investigate repeated diffs before widening tolerances. Pixel diffs are sensitive to rendering changes; an image mismatch signals that pixels changed, not by itself whether the change is a defect.

Or skip the browser setup

If you need screenshots of live pages outside the Cypress visual-test workflow, ScreenshotNeo is a website screenshot API and MCP server. A single GET request returns an image or PDF. This is not a replacement for comparing Cypress baselines: it is an option for capturing pages without configuring a browser in your own code.

See the ScreenshotNeo API documentation for parameters and response details. This cURL example saves a WebP capture of Stripe:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Cookie banners are accepted and removed before the capture, along with known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. AI agents can use the MCP server’s take_screenshot, get_page_info, and capture_pdf tools. The free plan includes 1,000 screenshots a month without a card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does Cypress compare screenshots automatically?

No. Cypress captures screenshots, including screenshots of failed tests, but baseline comparison requires a plugin or visual testing integration.

Should every Cypress test have a visual assertion?

No. Choose intentional visual checkpoints where appearance matters and where a reviewer can interpret a changed image.

Can a passing visual comparison prove the page is accessible?

No. Pixel comparison does not validate semantic structure, keyboard behavior, or whether text has sufficient contrast. Pair it with functional and accessibility checks.

Can I use Cypress screenshots as a substitute for baselines?

cy.screenshot() can save a capture, but by itself it does not compare that capture with an approved image or manage baseline review.

Sources