tldr: Cypress has no built-in visual regression testing. You need plugins or cloud services to catch visual bugs, and each option comes with real trade-offs in setup complexity, CI reliability, and cost.


Cypress doesn't do visual testing out of the box

This surprises people. Cypress ships with cy.screenshot(), but that just saves a PNG to disk. There's no comparison engine. No baseline management. No diff viewer.

If you want visual regression testing in Cypress, you're choosing between open-source plugins and paid cloud integrations. Both paths work. Neither is turnkey.

Playwright has expect(page).toHaveScreenshot() baked into its test runner. Cypress has nothing equivalent. That gap has existed since Cypress launched, and it's still there in 2026.


Plugin option 1: cypress-visual-regression

The cypress-visual-regression npm package is the simplest entry point. Two modes: base to generate baseline screenshots, regression to compare future runs against those baselines.

Installation

yarn add -D cypress-visual-regression

Configure the plugin

Add to your cypress.config.ts:

import { defineConfig } from 'cypress';
import getCompareSnapshotsPlugin from 'cypress-visual-regression/dist/plugin';

export default defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      getCompareSnapshotsPlugin(on, config);
    },
    env: {
      screenshotsFolder: './cypress/snapshots/actual',
      trashAssetsBeforeRuns: true,
      type: 'regression', // or 'base' for first run
    },
  },
});

Add the command to cypress/support/e2e.ts:

import compareSnapshotCommand from 'cypress-visual-regression/dist/command';

compareSnapshotCommand();

Write a visual test

describe('Homepage visual regression', () => {
  it('should match the baseline screenshot', () => {
    cy.visit('/');
    cy.compareSnapshot('homepage', 0.1); // 0.1 = 10% error threshold
  });

  it('should match the pricing section', () => {
    cy.visit('/pricing');
    cy.get('[data-testid="pricing-table"]').compareSnapshot('pricing-table', 0.05);
  });
});

Workflow

First run uses type: 'base' to generate reference images. Switch to type: 'regression' for subsequent runs. When screenshots differ beyond the threshold, the test fails and saves the actual, baseline, and diff images.

The plugin is straightforward. It works. But it has limits. Threshold is a single number. There's no built-in diff viewer in the Cypress UI. And cross-platform inconsistencies (fonts rendering differently on macOS vs. Linux CI) will cause false positives.


Plugin option 2: cypress-plugin-visual-regression-diff (FRSOURCE)

This is the more capable open-source option. FRSOURCE's plugin adds configurable thresholds, an inline diff preview directly in the Cypress Test Runner UI, and support for both e2e and component testing.

Installation

yarn add -D @frsource/cypress-plugin-visual-regression-diff

Configuration

In cypress.config.ts:

import { defineConfig } from 'cypress';
import { initPlugin } from '@frsource/cypress-plugin-visual-regression-diff/plugins';

export default defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      initPlugin(on, config);
    },
  },
});

In cypress/support/e2e.ts:

import '@frsource/cypress-plugin-visual-regression-diff';

Usage

describe('Dashboard visual tests', () => {
  it('matches the sidebar layout', () => {
    cy.visit('/dashboard');
    cy.get('[data-testid="sidebar"]').matchImage({
      maxDiffThreshold: 0.1,
      title: 'dashboard-sidebar',
    });
  });

  it('matches the full page', () => {
    cy.visit('/dashboard');
    cy.matchImage({
      maxDiffThreshold: 0.05,
    });
  });
});

Component testing support

This plugin also works with Cypress Component Testing. That's useful if you want to catch visual regressions in isolated components without spinning up the full app.

import { mount } from 'cypress/react';
import { Button } from '@/components/ui/button';

describe('Button component', () => {
  it('renders primary variant correctly', () => {
    mount(<Button variant="default">Click me</Button>);
    cy.get('button').matchImage({
      maxDiffThreshold: 0.01,
    });
  });
});

The inline diff viewer is the real selling point. When a test fails, you see the baseline, actual, and pixel diff side-by-side inside Cypress. No need to dig through folders or open separate files. This alone makes it a better choice than the basic cypress-visual-regression package for most teams.


Plugin option 3: cypress-visual-regression-resemble-js

Cross-platform rendering differences are the most annoying problem in visual regression testing. Your macOS laptop renders fonts one way. Your Linux CI runner renders them slightly differently. The screenshots never match pixel-for-pixel, even when nothing changed.

cypress-visual-regression-resemble-js addresses this by using Resemble.js under the hood. Resemble.js has built-in anti-aliasing detection. It can distinguish between actual visual changes and rendering differences caused by the OS environment.

Installation

yarn add -D cypress-visual-regression-resemble-js

Configuration

import { defineConfig } from 'cypress';
import { initResemblePlugin } from 'cypress-visual-regression-resemble-js/dist/plugin';

export default defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      initResemblePlugin(on, config);
    },
  },
});

In your support file:

import 'cypress-visual-regression-resemble-js';

Usage

describe('Visual regression with anti-aliasing tolerance', () => {
  it('compares the login page', () => {
    cy.visit('/login');
    cy.compareSnapshot('login-page', {
      errorThreshold: 0.05,
      ignore: 'antialiasing',
    });
  });
});

The ignore: 'antialiasing' option is the key differentiator. It tells the comparison engine to skip pixels that look like anti-aliasing artifacts. This dramatically reduces false positives when your local machine runs macOS and your CI runs Ubuntu.

If cross-environment consistency is your biggest pain point, this is the plugin to use.


Cloud integrations for Cypress visual testing

Open-source plugins store baselines locally or in your repo. That works for small projects. For larger teams, cloud-based visual regression testing tools offer baseline management, approval workflows, and cross-browser comparisons.

Percy (BrowserStack)

Percy is the most widely adopted cloud visual testing tool for Cypress. The Percy Cypress SDK integrates with your existing test suite.

yarn add -D @percy/cli @percy/cypress

Add to cypress/support/e2e.ts:

import '@percy/cypress';

Write tests:

describe('Percy visual tests', () => {
  it('captures the homepage', () => {
    cy.visit('/');
    cy.percySnapshot('Homepage');
  });

  it('captures the checkout flow', () => {
    cy.visit('/checkout');
    cy.get('[data-testid="order-summary"]').should('be.visible');
    cy.percySnapshot('Checkout - Order Summary');
  });
});

Run with the Percy CLI:

npx percy exec -- cypress run

Percy uploads snapshots to their cloud, renders them across browsers and viewport sizes, compares against baselines, and gives you a web dashboard to approve or reject changes. Pricing starts at $399/month for 25,000 screenshots.

Chromatic

Chromatic is best known for Storybook visual testing, but their Cypress plugin captures archives during Cypress test runs. It's a good fit if your team already uses Storybook and wants to consolidate visual testing into one platform.

yarn add -D chromatic
npx chromatic --cypress

Chromatic captures the DOM state (not just screenshots) during your Cypress tests. This means it can re-render the page in its own infrastructure and compare across browser engines. The archive-based approach is more resilient to rendering differences than pixel comparison.

Applitools Eyes

Applitools uses AI-based comparison instead of pixel diffing. Their Cypress SDK integrates similarly to Percy.

yarn add -D @applitools/eyes-cypress
npx eyes-setup
describe('Applitools visual tests', () => {
  it('validates the dashboard', () => {
    cy.eyesOpen({
      appName: 'My App',
      testName: 'Dashboard',
    });
    cy.visit('/dashboard');
    cy.eyesCheckWindow('Dashboard loaded');
    cy.eyesClose();
  });
});

Applitools' AI comparison ignores minor rendering differences automatically. It groups visual changes by component and lets you approve groups of changes at once. The trade-off is cost. Applitools is the most expensive option, and pricing isn't transparent.


Choosing between plugins and cloud services

FactorOpen-source pluginsCloud services
CostFree$399-$2,000+/month
Setup time15-30 minutes30-60 minutes
Baseline storageLocal filesystem or gitCloud managed
Cross-browser testingNo (Cypress is Chromium-only)Yes (Percy, Chromatic)
Approval workflowManual file comparisonWeb dashboard
Anti-aliasing handlingResemble.js plugin onlyBuilt-in for all
CI integrationDIYBuilt-in

For solo developers and small teams, start with the FRSOURCE plugin. It's free, the diff viewer is excellent, and it covers both e2e and component tests.

For teams with 5+ engineers shipping daily, a cloud service pays for itself. The approval workflow alone saves hours per week. Percy is the most battle-tested. Chromatic is ideal if you're already on Storybook.


Cypress vs. Playwright for visual regression testing

This comparison matters because it affects your framework choice, not just your plugin choice.

Playwright has built-in visual regression testing. One line of code:

await expect(page).toHaveScreenshot('homepage.png', {
  maxDiffPixelRatio: 0.01,
});

No plugins. No extra dependencies. Built into the test runner. Baseline management included. Diff viewer included.

Cypress needs a plugin for any of that. Here's what the comparison looks like across key dimensions.

Browser coverage

Playwright supports Chromium, Firefox, and WebKit natively. You can run visual regression tests across all three in the same test suite. Cypress runs exclusively on Chromium-based browsers. If you need Firefox or Safari visual testing, Cypress can't help without a cloud service like Percy or Chromatic rendering in those engines.

Snapshot management

Playwright stores baselines in a __snapshots__ directory next to your tests. Updating baselines is a single flag: --update-snapshots. Cypress plugins each handle storage differently. Some use cypress/snapshots/, others use custom paths. Updating baselines means switching env variables or deleting files manually.

Threshold configuration

Playwright offers maxDiffPixels, maxDiffPixelRatio, and threshold (per-pixel color difference). All configurable per assertion. Cypress plugins typically offer a single error threshold percentage. FRSOURCE's maxDiffThreshold is the closest to Playwright's granularity, but it's still less flexible.

CI reliability

Playwright's screenshot comparison is optimized for CI. It handles anti-aliasing differences between environments reasonably well out of the box. Cypress plugins vary. Without the Resemble.js plugin, you'll fight false positives on every CI run that uses a different OS than your local machine.

The verdict

If visual regression testing is a primary requirement and you're choosing a framework today, Playwright has a clear advantage. If you're already committed to Cypress and adding visual testing later, the plugin ecosystem gets the job done. Just expect more setup and maintenance.


Setting up Cypress visual regression in CI

Getting visual tests passing locally is the easy part. CI is where things break.

Docker for consistent baselines

The number one cause of flaky visual tests in CI is rendering differences. Fix this by running Cypress in Docker, both locally and in CI.

FROM cypress/included:13.7.0

WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .

Generate baselines inside the Docker container:

docker build -t cypress-visual .
docker run -e CYPRESS_type=base cypress-visual yarn cypress run

Run regression checks in CI using the same image:

docker run -e CYPRESS_type=regression cypress-visual yarn cypress run

Same OS. Same fonts. Same rendering engine. No false positives from environment differences.

GitHub Actions example

name: Visual Regression Tests
on: [pull_request]

jobs:
  visual-test:
    runs-on: ubuntu-latest
    container:
      image: cypress/included:13.7.0
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Run visual regression tests
        run: yarn cypress run --spec "cypress/e2e/visual/**/*.cy.ts"
        env:
          type: regression

      - name: Upload diff artifacts
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: visual-diffs
          path: cypress/snapshots/diff/

Handling baseline updates

When you intentionally change the UI, baselines need updating. Two approaches:

Approach 1: Commit baselines to git. Run baseline generation locally in Docker, commit the new images, push. Simple but bloats your repo. Each PNG is 50-200KB. A hundred baselines adds 5-20MB of binary files to your history.

Approach 2: Store baselines in CI artifacts or cloud storage. Upload baselines to S3 or GCS after generation. Download them before regression runs. Keeps the repo clean but adds CI complexity.

Most teams start with approach 1 and move to approach 2 when the repo gets unwieldy.


Common pitfalls and how to avoid them

Animations cause diff failures

Animations are the top source of false positives. A screenshot captured mid-animation will never match the baseline.

// Disable animations before taking screenshots
cy.visit('/', {
  onBeforeLoad(win) {
    const style = win.document.createElement('style');
    style.innerHTML = `
      *, *::before, *::after {
        animation-duration: 0s !important;
        transition-duration: 0s !important;
      }
    `;
    win.document.head.appendChild(style);
  },
});
cy.compareSnapshot('homepage');

Dynamic content breaks comparisons

Timestamps, user avatars, ads, and live data change between runs. Mask or hide these elements before capturing.

cy.visit('/dashboard');

// Hide dynamic elements
cy.get('[data-testid="timestamp"]').invoke('css', 'visibility', 'hidden');
cy.get('[data-testid="user-avatar"]').invoke('css', 'visibility', 'hidden');

cy.compareSnapshot('dashboard-static');

Viewport inconsistency

Always set the viewport explicitly. Different default viewport sizes between local and CI will cause every screenshot to fail.

// In cypress.config.ts
export default defineConfig({
  viewportWidth: 1280,
  viewportHeight: 720,
  // ...
});

Waiting for page load

Screenshots captured before the page finishes loading produce inconsistent results. Wait for specific elements or network idle states.

cy.visit('/dashboard');
cy.get('[data-testid="dashboard-loaded"]').should('be.visible');
cy.intercept('GET', '/api/data').as('getData');
cy.wait('@getData');
cy.compareSnapshot('dashboard');

When Cypress visual testing isn't enough

Pixel-based visual regression catches layout shifts, color changes, and missing elements. It doesn't catch usability issues, broken interactions, or visual problems that only appear during specific user flows.

If your team needs visual coverage but doesn't have the bandwidth to set up and maintain Cypress plugins, CI pipelines, and baseline management, Bug0 Studio handles visual and functional testing through AI agents that navigate your app and flag visual anomalies without any screenshot infrastructure.

For teams without dedicated QA headcount, Bug0 Managed provides forward-deployed engineers who own the entire testing pipeline. That includes visual regression, functional flows, and cross-browser coverage.

For open-source alternatives, tools like BackstopJS, Loki, and reg-suit offer visual regression outside the Cypress ecosystem. And if you're evaluating broader AI-powered UI testing, the landscape has shifted toward autonomous agents that don't rely on pixel comparison at all.


FAQs

Does Cypress have built-in visual regression testing?

No. Cypress provides cy.screenshot() for capturing images, but there's no built-in comparison engine, baseline management, or diff viewer. You need a plugin like cypress-visual-regression, FRSOURCE's cypress-plugin-visual-regression-diff, or a cloud service like Percy.

Which Cypress visual regression plugin should I use?

For most teams, start with FRSOURCE's cypress-plugin-visual-regression-diff. It has the best diff viewer, supports both e2e and component testing, and offers configurable thresholds. If cross-platform rendering differences are your main problem, use cypress-visual-regression-resemble-js for its anti-aliasing detection.

How do I avoid false positives in Cypress visual regression tests?

Three things: disable animations before capturing screenshots, hide dynamic content (timestamps, avatars, ads), and run tests in Docker to eliminate OS-level rendering differences. Setting an explicit viewport size in your Cypress config also prevents dimension-related mismatches.

Can I run Cypress visual regression tests in CI?

Yes, but use Docker containers to ensure consistent rendering between local and CI environments. The cypress/included Docker images are the standard choice. Store baselines either in git (simple) or in cloud storage (scalable). Always upload diff artifacts on failure so you can debug without re-running.

How does Cypress visual testing compare to Playwright?

Playwright has built-in visual regression testing with toHaveScreenshot(). No plugins needed. It also supports Chromium, Firefox, and WebKit, giving you cross-browser visual coverage natively. Cypress needs plugins for basic comparison and only runs on Chromium. If visual testing is a primary concern, Playwright has the advantage.

Is Percy worth the cost for Cypress visual testing?

Percy is worth it for teams with 5+ engineers shipping frequently. The approval dashboard, cross-browser rendering, and automatic baseline management save significant time compared to open-source plugins. At $399/month for 25,000 screenshots, it's reasonable for mid-size teams. Solo developers and small teams can get by with free plugins.

Can I do component-level visual testing in Cypress?

Yes. FRSOURCE's plugin supports Cypress Component Testing. You can mount individual components and run visual comparisons against baselines. This catches regressions in design system components without needing the full application running.

What's the biggest limitation of Cypress for visual regression testing?

Browser coverage. Cypress only runs on Chromium-based browsers. You can't visually test how your app looks in Firefox or Safari natively. Cloud services like Percy and Chromatic can render in additional browsers, but that adds cost and complexity. Playwright solves this natively with its multi-browser support.