ScreenshotNeo

BlogHow-to

How to Use the Cypress Image Snapshot Plugin

Install, configure, compare, update, and troubleshoot Cypress image snapshots with reproducible baselines and CI workflows.

By the ScreenshotNeo team29 September 20269 min read

How to Use the Cypress Image Snapshot Plugin

Direct answer: install @simonsmith/cypress-image-snapshot, register its Node event plugin in cypress.config.ts, register its custom command in your Cypress support file, then call cy.matchImageSnapshot() after the page reaches the visual state you want to verify. The plugin compares the new screenshot with a saved baseline, writes a diff when pixels differ, and fails the test by default.

The workflow is local and repository-friendly: baseline images live under your project, diffs can be reviewed in CI artifacts, and updates are explicit. Keep the browser, operating system, fonts, viewport, and application data stable so a real UI change is not confused with rendering noise. Cypress recommends generating and comparing screenshots in the same environment with a fixed viewport (Cypress visual testing guidance).

1. Install the plugin

Add the package as a development dependency:

npm install --save-dev @simonsmith/cypress-image-snapshot
# or
yarn add --dev @simonsmith/cypress-image-snapshot

The package expects Cypress to be installed as a peer dependency. The README says it has been tested with Cypress 13.x and 14.x, while the current Cypress plugin directory listing identifies version 11.0.0 as requiring Cypress 15.10.0 or newer. Those signals do not line up, so inspect the exact package metadata installed in your project before upgrading Cypress or the plugin. See the plugin README and your installed package’s peer dependencies.

2. Register the Node event plugin

In a TypeScript project, update cypress.config.ts:

import { defineConfig } from 'cypress'
import { addMatchImageSnapshotPlugin } from '@simonsmith/cypress-image-snapshot/plugin'

export default defineConfig({
  e2e: {
    setupNodeEvents(on) {
      addMatchImageSnapshotPlugin(on)
    },
  },
})

If you already have a setupNodeEvents function, keep your existing tasks and return values and call addMatchImageSnapshotPlugin(on) inside that function. Do not create a second Cypress configuration file just for the snapshot plugin.

3. Register the custom command

Import the command once in the support file used by the tests. For end-to-end tests this is commonly cypress/support/e2e.ts:

import { addMatchImageSnapshotCommand } from '@simonsmith/cypress-image-snapshot/command'

addMatchImageSnapshotCommand()

You can set defaults while registering the command. This example applies a shared failure threshold:

import { addMatchImageSnapshotCommand } from '@simonsmith/cypress-image-snapshot/command'

addMatchImageSnapshotCommand({ failureThreshold: 0.2 })

TypeScript projects can add @simonsmith/cypress-image-snapshot/types to tsconfig.json so the custom command is included in type checking. The package includes TypeScript declarations.

4. Capture a first baseline

Drive the application into a deterministic state before taking the snapshot. The command should be the assertion at the end of the visual setup:

The test drives the page to a stable state before the plugin compares the current screenshot with its baseline.
The test drives the page to a stable state before the plugin compares the current screenshot with its baseline.
describe('login page', () => {
  it('matches the login view', () => {
    cy.visit('/login')
    cy.get('input[name="email"]').type('user@example.test')
    cy.get('input[name="password"]').type('correct horse battery staple')
    cy.get('button[type="submit"]').focus()

    cy.matchImageSnapshot()
  })
})

With no argument, the snapshot name is derived from the Cypress test title. You can provide a stable name explicitly:

cy.matchImageSnapshot('login')
cy.matchImageSnapshot('auth/login-filled')

To compare only one element, use the command on a Cypress subject:

cy.get('#login').matchImageSnapshot()

Run the spec once to create the baseline. The documented layout stores images below <rootDir>/cypress/snapshots. Commit accepted baselines with the test code so another checkout has the same expected images.

5. Understand comparison output

Each run captures a Cypress screenshot and compares it with the saved image. When pixels differ, the plugin writes a generated diff under <rootDir>/cypress/snapshots/__diff_output__. A mismatch fails the test by default. Keep the current screenshot and diff as CI artifacts when possible; they show whether the change is an intentional design update, a data problem, or rendering drift.

The command accepts settings from the underlying jest-image-snapshot comparison layer and Cypress screenshot configuration. Common examples include:

cy.matchImageSnapshot('dashboard', {
  failureThreshold: 0.01,
  comparisonMethod: 'ssim',
  capture: 'viewport',
  blackout: ['.live-clock', '[data-testid="random-avatar"]'],
})
  • failureThreshold: controls the tolerated difference according to the comparison configuration. Use a small, documented value and review the resulting diff rather than hiding broad changes.
  • comparisonMethod: the README shows ssim as an available example in addition to the default comparison behavior.
  • capture: choose the Cypress capture mode, such as viewport, when you want to constrain the image to the visible browser area.
  • blackout: masks selectors that contain clocks, rotating content, ads, or other intentionally changing pixels.

Pass options per snapshot when only one state needs special handling; register defaults when every test in a support file follows the same policy.

6. Control baseline updates deliberately

Updating snapshots should be a review operation. Generate new images, inspect the diff, and commit only the baselines that represent an intended UI change. The plugin documents these Cypress command-line controls:

Purpose Cypress 15.10+ Older Cypress versions
Update baselines --expose updateSnapshots=true --env updateSnapshots=true
Do not fail on a diff --expose failOnSnapshotDiff=false --env failOnSnapshotDiff=false
Require snapshots to exist --expose requireSnapshots=true --env requireSnapshots=true

For example, a deliberate update on newer Cypress releases can look like:

npx cypress run --expose updateSnapshots=true

Use requireSnapshots=true in CI when missing baselines should fail instead of silently creating new ones. Use failOnSnapshotDiff=false for an inspection job, not as a replacement for reviewing visual changes.

7. Keep snapshot paths predictable

Snapshot names may include nested paths. For Cypress 10 and newer, common ancestor paths were removed from generated screenshots. The plugin’s e2eSpecDir option, which defaults to cypress/e2e/, can preserve the intended relationship between spec files and snapshot directories. Match e2eSpecDir to the directory structure used by your specPattern.

import { addMatchImageSnapshotCommand } from '@simonsmith/cypress-image-snapshot/command'

addMatchImageSnapshotCommand({
  e2eSpecDir: 'cypress/e2e/',
})

If snapshots appear in an unexpected folder after a Cypress upgrade, compare your spec path, specPattern, and e2eSpecDir before renaming files. A path mismatch can make an existing baseline look missing even though it is present elsewhere.

8. Make visual comparisons reproducible

Visual assertions are only useful when the rendering inputs are controlled. Apply this checklist:

  • Set a fixed viewport in Cypress configuration or before the test.
  • Run baseline generation and comparison with the same browser and operating-system image.
  • Use deterministic fixtures, dates, seeded data, and stable authentication state.
  • Wait for the page’s meaningful state: a route, selector, API result, or animation completion.
  • Disable or mask clocks, rotating banners, randomized avatars, cursor indicators, and live counters.
  • Load the same fonts in baseline and comparison runs; font fallback changes many pixels.
  • Keep device scale and browser versions consistent in CI.

For example:

cy.viewport(1280, 800)
cy.visit('/reports')
cy.get('[data-testid="report-table"]').should('be.visible')
cy.get('[data-testid="loading"]').should('not.exist')
cy.matchImageSnapshot('reports-ready', {
  capture: 'viewport',
  blackout: ['[data-testid="last-updated"]'],
})

9. CI workflow and review policy

Keep snapshots in version control and run the same Cypress command in pull requests. Store failed screenshots and __diff_output__ as build artifacts. A practical review sequence is:

  1. Read the failing test and identify the changed route or component.
  2. Open the current image, baseline, and diff together.
  3. Decide whether the difference is a product change, unstable test data, or environment drift.
  4. Fix unstable inputs first; update the baseline only when the product change is intentional.
  5. Commit the baseline and test change together.

For component snapshots, keep one focused assertion per meaningful state. For full-page snapshots, use them for layout and integration coverage rather than every small component variant; large images are slower to review and more sensitive to unrelated changes.

10. Troubleshooting common failures

Symptom Likely cause Fix
cy.matchImageSnapshot is not a function The support import did not run for this testing type. Import addMatchImageSnapshotCommand in the support file referenced by your Cypress configuration, then restart the Cypress process.
Plugin setup error during startup The Node event function was not registered, or the import path/version is wrong. Call addMatchImageSnapshotPlugin(on) inside setupNodeEvents and inspect the installed package version.
Every run produces a diff Viewport, browser, fonts, animation, time, or data differs. Fix the environment, wait for a stable selector, freeze dynamic content, and use blackout only for truly irrelevant regions.
Baseline is reported missing Snapshot path mapping changed or CI checked out no baseline files. Verify e2eSpecDir, specPattern, snapshot naming, and that cypress/snapshots is committed.
Headless and headed images differ Different browser flags, fonts, device scale, or window dimensions. Standardize the CI browser image and viewport; generate and compare in the same mode.
Long pages are clipped The selected capture mode is viewport-only or the element has layout constraints. Use the appropriate Cypress screenshot capture setting and verify the page has finished layout before the command.
Small harmless changes fail tests Pixel comparison is too strict for the content. Use a documented threshold or comparisonMethod: 'ssim', then inspect diffs to ensure meaningful regressions still fail.

11. Performance, reliability, and cost considerations

Local snapshots add browser screenshot and image-comparison work to each Cypress run. Reduce runtime by taking focused element images where full-page coverage is unnecessary, waiting on one reliable readiness condition instead of arbitrary long delays, and avoiding duplicate snapshots of identical states. Keep artifacts only for failed or changed comparisons if storage is constrained.

Reliability comes primarily from environment control. A baseline is an image file, so it cannot explain why a test changed; pair it with deterministic fixtures and readable test names. When upgrading Cypress, browsers, fonts, or operating-system images, expect a deliberate baseline review.

The plugin itself is a development dependency and keeps comparison data in your repository or CI storage. A hosted visual-testing service may instead provide cloud rendering, image storage, comparison, and review workflows. Cypress describes Percy and Sauce Labs Visual integrations in this context; evaluate browser and viewport coverage, data ownership, review workflow, rendering consistency, and subscription cost for your project.

12. Or skip the browser setup

If you need a clean screenshot from a URL rather than a repository-managed Cypress baseline, ScreenshotNeo provides a website screenshot API and MCP server. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. Cookie and consent banners are accepted before capture, and more than 60 known consent platforms, newsletter popups, and chat widgets can be removed; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result.

ScreenshotNeo can remove common consent banners, popups, and chat widgets before capture.
ScreenshotNeo can remove common consent banners, popups, and chat widgets before capture.

See the ScreenshotNeo API documentation for the full option set. A minimal request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Options cover full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper size and page ranges, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, and a usage API. An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

There are 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account.

FAQ

Does the plugin compare full pages or elements?

Both. Call cy.matchImageSnapshot() for the configured Cypress screenshot and call cy.get(selector).matchImageSnapshot() for an element.

Where should baselines be committed?

The documented default is under cypress/snapshots. Commit accepted images with the test so CI can require them.

How do I update snapshots safely?

Run with the documented update flag, inspect each diff, and commit only intentional visual changes. Use requireSnapshots=true in normal CI to catch missing files.

Why does Cypress version compatibility look inconsistent?

The README and current directory metadata report different compatibility signals. Check the exact installed release’s peer dependency and changelog before changing versions.

Can I use this without storing images in Git?

The plugin’s documented workflow stores baselines under the project root. A hosted service may fit better when you need managed storage and review, but compare its rendering, retention, and pricing model with your repository workflow.