ScreenshotNeo

BlogHow-to

How to Use Percy with Cypress for Visual Regression Testing

Add Percy snapshots to Cypress, run them in CI, and keep visual diffs reliable with stable page state, test data, and server readiness.

By the ScreenshotNeo team4 October 20268 min read

To use Percy with Cypress, install @percy/cli and @percy/cypress, import the Cypress integration in your configured support file, and call cy.percySnapshot() after the page has reached the state you want to check. Set the Percy project token as PERCY_TOKEN and run Cypress through npx percy exec -- cypress run. Percy then receives snapshots for a build that you can review and approve.

This guide assumes an existing Cypress project and npm. Use the equivalent package manager commands if your repository uses Yarn, pnpm, or another package manager. See the Percy Cypress SDK README, Cypress visual testing documentation, and Cypress CI documentation for project-specific and current details.

1. Install Percy and connect it to Cypress

Install both packages as development dependencies:

npm install --save-dev @percy/cli @percy/cypress

Import the integration from Cypress’s support entry point. The Percy repository README demonstrates cypress/support/index.js; your project may use a different support file, so check the support-file setting in its Cypress configuration.

// cypress/support/e2e.js, or your configured support entry point
import '@percy/cypress'

If your project uses CommonJS, use the equivalent import syntax supported by its Cypress setup:

require('@percy/cypress')

Keep the import in a file Cypress loads for the tests that call cy.percySnapshot(). If the command is reported as unknown, this support-file connection is the first thing to check.

2. Add a snapshot at a stable UI state

Call cy.percySnapshot() after visiting the page and asserting that it has reached the intended state. The assertion helps ensure the capture does not happen while the page is still loading or changing.

describe('Account page', () => {
  it('shows the signed-in state', () => {
    cy.visit('/account')
    cy.get('[data-testid="account-ready"]').should('be.visible')
    cy.percySnapshot('Account page: signed in')
  })
})

Replace the route and readiness selector with ones from your application. Use a meaningful, repeatable snapshot name; when you omit the name, the integration README says the full test title is used by default. Make the test establish the state deliberately: log in or seed data using your normal test setup, wait for the relevant content, and then capture.

Cypress’s guidance is direct: “Best Practice: Take a snapshot only after you confirm the page is done changing.” A snapshot taken during animation, asynchronous loading, or a transient error can create a diff that says more about timing than an application change.

3. Run locally and upload a Percy build

Set the project token in your shell or secret manager. Do not commit a real token to source control.

export PERCY_TOKEN=your_project_token
npx percy exec -- cypress run

percy exec wraps the Cypress command so the Percy process can create a build and receive snapshots. Running Cypress without that Percy process disables the Percy snapshot upload workflow. Keep the token in your CI provider’s secret store when running remotely.

After the run, open the corresponding build in Percy to inspect visual differences and approve intended changes according to your team’s review process. Percy adds snapshot collection and hosted comparison/review to Cypress’s test-driving role. The Cypress documentation describes rendering snapshots across browsers and responsive widths in Percy; confirm the current project configuration and service behavior in the vendor documentation.

4. Configure CI so snapshots are repeatable

A reliable CI sequence starts the application, waits until it responds, and then runs Cypress inside Percy. Do not start a server in the background and immediately launch tests: Cypress warns that this creates a race when the server is not ready yet. Cypress documents readiness options including start-server-and-test, wait-on, and the Cypress GitHub Action’s start and wait-on settings.

For example, with start-server-and-test, install it as a development dependency and define scripts that wait for the app URL before invoking the tests:

npm install --save-dev start-server-and-test
{
  "scripts": {
    "dev:ci": "your-app-start-command",
    "cy:run": "percy exec -- cypress run",
    "test:e2e": "start-server-and-test dev:ci http://localhost:3000 cy:run"
  }
}

Replace your-app-start-command and the URL with the actual application start command and a route that returns successfully only when the app is ready. Configure PERCY_TOKEN as a CI secret, then invoke npm run test:e2e. This script is a pattern; adapt it to the server command, health endpoint, package manager, and CI environment in your repository.

  1. Install dependencies and the Cypress binary as required by your CI setup.
  2. Start the application using the command intended for the test environment.
  3. Wait for an application readiness URL or equivalent health check.
  4. Run percy exec -- cypress run with the token supplied from CI secrets.
  5. Review the Percy build and decide whether diffs are intended before updating a baseline.

See the Cypress CI overview for CI setup guidance. Avoid replacing a readiness check with a fixed sleep: machine load and startup time vary, so a sleep can be either wasteful or too short.

5. Make visual comparisons useful

  • Wait for the target state. Assert on a stable, meaningful element before snapshotting. For interactions, complete the action and wait for the resulting UI.
  • Control test data. Use predictable data so content, ordering, and counts do not shift between runs.
  • Control time-dependent content. Freeze or otherwise stabilize dates, rotating content, and other values that naturally change when the test runs.
  • Keep rendering conditions consistent. Use the same test setup and comparable viewport and browser conditions when reviewing changes.
  • Capture meaningful screens. Prefer important pages or components at useful states over every transient state. Each snapshot should answer a review question.
  • Name snapshots clearly. Names that identify a page and state are easier to review than generic names.
  • Review before approving. A visual diff can represent an intended design change, a rendering variation, or a test setup problem; inspect the change before accepting it.

Cypress’s visual-testing guide lists local screenshot comparison plugins as well as hosted services, including Percy, Chromatic, Happo, LambdaTest SmartUI, Sauce Labs Visual, SmartBear VisualTest, and Wopee.io. Percy is not required for Cypress visual testing. Compare tools by whether they capture screenshots, DOM snapshots, or archived UI; whether rendering and comparison are local or hosted; browser and viewport coverage; baseline update and approval workflow; CI fit; data handling; and current pricing and contract terms. The cited sources do not establish current pricing for these options, so verify terms with each provider.

6. Troubleshooting

Symptom Likely cause Fix
cy.percySnapshot is not a function or the command is unknown The Percy Cypress integration is not imported by the support file used by this test, or the package is missing. Confirm @percy/cypress is installed and imported from the configured Cypress support entry point. Check Cypress’s support-file configuration and rerun.
Cypress passes, but Percy has no snapshots or build Cypress was run without the Percy process, or the project token is absent or invalid. Run through npx percy exec -- cypress run, and make sure PERCY_TOKEN is available to that process. Check the Percy CLI output for upload errors.
Local snapshots do not upload from CI The CI job does not expose the token to the test step, or the Percy-wrapped command is not the command CI invokes. Store the token as a CI secret, map it to PERCY_TOKEN in the step that runs tests, and inspect the actual CI command and logs. Never print the token in logs.
Page is blank, incomplete, or intermittently different The snapshot ran before the app or asynchronous content was ready, or the app server was not ready before Cypress started. Add a functional readiness assertion before the snapshot and a server readiness check before Cypress. Stabilize test data and time-dependent content.
Diffs appear without an intentional UI change Rendering conditions or test setup differ, or the capture includes dynamic content, animation, or an unstable state. Compare the test state and rendering conditions, wait for the UI to settle, and control dynamic content. Update the baseline only after confirming the visual change is intended.
CI exits before the server accepts requests The server starts asynchronously and the test command begins immediately. Use a readiness tool or the Cypress GitHub Action’s start/wait-on options instead of launching the server and tests concurrently without a gate.
Snapshot names are difficult to map to screens Names are omitted or generic. Pass a descriptive name to cy.percySnapshot('Page: state'); otherwise the documented default is the full test title.

7. Performance, reliability, and cost considerations

Every snapshot adds work to the test and hosted comparison workflow, so capture the states that protect meaningful behavior. A focused set of snapshots is easier to review and less likely to include accidental transient states. The supplied documentation does not establish a universal runtime or speed benchmark; measure the effect in your own CI run and check your current Percy plan and project limits for applicable usage or cost terms.

Reliability depends heavily on test determinism: the app must be ready, data must be predictable, and the captured state must have stopped changing. A passing functional assertion is useful evidence that the page reached the intended state, though it does not by itself guarantee every visual resource has settled. Review CI output and Percy build status when uploads fail.

For a different kind of screenshot workflow, ScreenshotNeo is a website screenshot API and MCP server. It returns PNG, JPEG, WebP, or PDF from a GET request; it is designed for screenshot capture and is not a replacement for Percy’s visual regression baselines and review workflow.

Or skip the browser setup

If you need a website screenshot from a URL without wiring up a browser in your own code, ScreenshotNeo can return an image with one GET request. The example below saves a WebP response.

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

See the ScreenshotNeo API documentation for request options and setup. The API supports PNG, JPEG, WebP, and PDF output, plus options such as full-page or CSS-selector capture, viewport and device settings, custom CSS or JavaScript, waiting for page conditions, custom headers and cookies, and caching. Cookie banners, newsletter popups, and chat widgets are removed before the shot; 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. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.

FAQ

Does Percy replace Cypress?

No. Cypress runs the tests and drives the application; Percy adds snapshot collection, hosted visual comparison, and a review and approval workflow.

Can I use Cypress visual testing without Percy?

Yes. Cypress documents open-source local screenshot comparison approaches and multiple commercial services. Choose based on capture method, rendering, baseline management, review, CI, and data requirements.

Should I snapshot every test?

No. Capture meaningful UI states whose appearance matters and whose data and rendering conditions you can keep stable.

Can Percy snapshots run in CI?

Yes. Provide the project token securely and run Cypress through percy exec after the app server is ready.

References