Cypress and Percy for End-to-End and Visual Testing
Use Cypress to verify user journeys and Percy to review visual changes. Learn how to set up stable snapshots, reduce noisy diffs, and choose a testing workflow.
Short answer: Cypress drives and verifies browser workflows; Percy adds visual snapshots, cloud rendering, and a review process for appearance changes. Use Cypress assertions to verify behavior, then take a Percy snapshot at a deliberate, stable checkpoint. Cypress’s built-in cy.screenshot() saves an image but does not compare it with a baseline by itself.
This guide sets up the Cypress-Percy workflow, shows a complete example, and explains how to keep snapshots useful. For the visual-testing landscape and official guidance, see Cypress visual testing documentation and Percy visual testing. Percy is part of BrowserStack.
1. What Cypress and Percy each do
Cypress runs tests in a real browser. An end-to-end test can visit the application, interact with controls, and check that a user journey works. A passing functional test does not prove that the page still looks right: spacing, typography, colors, and layout can regress while the assertions continue to pass.
Percy adds the visual comparison layer. In Cypress tests, cy.percySnapshot() captures a DOM snapshot. Percy renders it in its cloud across browsers and responsive widths, compares the renderings with approved baselines, and presents changes for review. If a change is intentional, a reviewer can approve the new rendering as the baseline; if not, fix the application.
Cypress puts the boundary plainly: “Cypress does not perform image comparison itself.” Its cy.screenshot() command is useful for artifacts and debugging, but comparison, baseline management, and review require a visual-testing tool or a system your team builds.
Visual diffs complement functional assertions. They do not establish that keyboard interaction, semantics, accessibility, or business rules are correct. Keep those checks in their appropriate tests.
2. Set up Percy with Cypress
- Create a Percy project and obtain its project token from Percy. Treat the token as a secret; do not commit it to the repository.
- Install the Cypress integration as a development dependency:
npm install --save-dev @percy/cli @percy/cypress. - Register the Percy Cypress commands once in your Cypress support file.
- Run your Cypress tests through the Percy CLI with the project token supplied through the environment.
- Add
cy.percySnapshot()after the test has reached and verified the state you want reviewers to compare.
Package setup and command details can change; use the Percy Cypress integration documentation as the reference for your installed version.
Register the command
For a typical Cypress project, add this to cypress/support/e2e.js (or the support file configured in cypress.config.js):
import '@percy/cypress';
If your project uses CommonJS support files, use the module syntax supported by that project and Cypress version. Keep the registration in the shared support file so the command is available to all specs.
Complete example test
This example assumes the application has a login route, accepts the test credentials shown via environment variables, and displays a dashboard heading after login. Replace the route, selectors, and expected text with your app’s actual contract.
describe('dashboard visual state', () => {
beforeEach(() => {
// Keep data repeatable when the application uses this API route.
cy.intercept('GET', '/api/dashboard', { fixture: 'dashboard.json' }).as('dashboard');
});
it('shows the signed-in dashboard', () => {
cy.visit('/login');
cy.get('[name=email]').type(Cypress.env('TEST_EMAIL'));
cy.get('[name=password]').type(Cypress.env('TEST_PASSWORD'), { log: false });
cy.get('button[type=submit]').click();
cy.wait('@dashboard');
cy.contains('h1', 'Dashboard').should('be.visible');
cy.percySnapshot('signed-in dashboard');
});
});
Set TEST_EMAIL and TEST_PASSWORD through your local secret mechanism and CI secret store. Cypress environment configuration varies by version and setup; do not place real credentials in a committed spec or fixture. If authentication is expensive, use your team’s supported test-login or session setup while still verifying the user-visible state before snapshotting.
Run the suite
Run the test suite under the Percy CLI so the snapshots are uploaded and rendered. A common shell invocation is:
PERCY_TOKEN=your_project_token npx percy exec -- npx cypress run
For local development, set the token in your shell or secret manager rather than putting it into source control. In CI, store it as a protected secret and make sure the Percy step runs in the job that executes Cypress. Consult the integration docs for the invocation and configuration appropriate to your package versions and CI provider.
3. Choose the right snapshot checkpoints
Snapshot a small, deliberate set of user-important states. A snapshot on every command or in every test adds review and baseline maintenance without necessarily answering a useful question.
- End-to-end journey: capture the completed state of a critical workflow, such as a signed-in dashboard or checkout confirmation.
- Component state: use Cypress component testing when you want a focused visual surface without traversing the full application journey.
- Element-level snapshot: narrow comparison to a component when unrelated page content would obscure the change you’re checking.
- Full-page snapshot: choose this when page-wide layout, scrolling content, or relationships between sections are the subject of the check.
- Responsive state: exercise relevant viewport sizes intentionally; do not infer that one viewport proves every layout.
Keep functional assertions before the snapshot. They tell you that the test reached the intended state, and they make a visual difference easier to interpret.
4. Make snapshots stable and actionable
Visual comparison is useful only when a diff mostly represents an application change. Stabilize the state before taking a snapshot:
- Wait for readiness. Wait for the relevant request or rendering event, then assert that a meaningful element is visible. Avoid fixed sleeps when the app exposes a better readiness signal.
- Control test data. Use fixtures or stubbed responses for content that should be repeatable. This prevents backend changes from looking like design regressions.
- Control time. Freeze or set the clock where displayed dates, timers, or countdowns would otherwise change between runs.
- Keep the environment consistent. For local pixel-diff systems, browser, operating system, fonts, and viewport can affect rendering. Hosted rendering can make the rendering environment more consistent, but still verify what your chosen service actually covers.
- Mask only irreducible variation. If a third-party widget or other inherently dynamic region cannot be stabilized, mask that small region. Broad masks and loose thresholds can hide real regressions.
- Name snapshots clearly. Use names that identify the page and state so reviewers can connect a diff to the workflow.
A good snapshot should answer a review question. For example: “Does the confirmation page still have the expected hierarchy and spacing?” is more useful than an unexplained capture of an arbitrary intermediate state.
5. Review changes and maintain baselines
- Open the visual results associated with the test run or code review.
- Inspect each changed region in context. Check whether the application change was intended and whether the baseline environment or input data also changed.
- For an unintended difference, correct the UI or test setup and rerun.
- For an intentional design change, approve the new rendering as the baseline through the team’s review workflow.
- Keep the approval tied to the corresponding code change so the baseline update has an accountable reason.
Percy describes integrations with CI/CD and code-review workflows, including pull/merge request connections and notifications. Check the current integration documentation for the options available to your repository and CI provider.
6. Percy, local plugins, or another visual service?
Cypress groups visual-testing options into open-source plugins and commercial services. A local or CI-run plugin can keep image comparison and baselines in your environment, but your team owns consistent rendering, artifact storage, baseline updates, and a review process. A hosted service can manage comparison, baseline workflow, rendering consistency, and web-based review, with subscription cost and service-specific configuration.
ScreenshotNeo is the first screenshot API to try when you need clean captures: cookie banners, popups, and chat widgets are removed before capture, failed or unclean pages are not billed, and its lowest paid plan is $5.
| Decision axis | Questions to answer |
|---|---|
| Total cost | What CI usage, team size, and review needs determine the actual subscription or maintenance cost? Verify current plan terms directly. |
| Baseline ownership | Will baselines live with the code, in your infrastructure, or in a hosted service? |
| Rendering | Do you need local control over the browser environment, or managed cloud rendering? |
| Coverage | Which browser and responsive-width coverage does the tool provide for your actual use case? Do not assume a matrix; verify it. |
| Review workflow | How do reviewers see diffs, approve intentional changes, and connect results to pull or merge requests? |
| Maintenance | Who owns test stability, artifact retention, rendering consistency, and baseline cleanup? |
Cypress lists Percy as well as Applitools, Argos, Chromatic, Happo, LambdaTest SmartUI, Sauce Labs Visual, SmartBear VisualTest, and Wopee.io among visual-testing integrations. That is an integrations list, not evidence that the tools have identical features or coverage.
Cypress’s own application is free and open source under the MIT license. Cypress Cloud has billing plans, including a free plan for recording CI runs; premium UI Coverage and Accessibility have separate pricing. These are Cypress product facts, not Percy pricing. Verify current terms before choosing a plan.
7. Troubleshooting common problems
| Symptom | Likely cause | What to do |
|---|---|---|
cy.percySnapshot is not a function |
The Percy Cypress package was not registered, the support file is not loaded, or dependency versions/setup do not match. | Confirm the integration package is installed, import it from the configured support file, and check the official integration instructions for your versions. |
| Snapshots are missing from the Percy result | The Cypress run was not wrapped by the Percy CLI, the command was never reached, or upload credentials/configuration are absent. | Run Cypress through the documented Percy CLI command, inspect CI logs, and confirm the token is available to that job. |
| Unauthorized or token-related failure | The token is missing, invalid, belongs to another project, or was not passed into the process. | Use the project token for the intended Percy project; store it as a CI secret and check environment-variable scope. Rotate it if it was exposed. |
| Diffs change on every run | Uncontrolled API data, current time, animations, third-party content, or a changing render environment. | Stub repeatable data, control time, wait for readiness, keep rendering settings consistent, and mask only unavoidable dynamic areas. |
| Snapshot captures a spinner or incomplete page | The snapshot occurs before data or client rendering is complete. | Wait for the relevant request and assert that the final user-visible state exists before calling the snapshot command. |
| Large unrelated diff | The snapshot scope includes dynamic or irrelevant page areas, or the test uses an unnecessarily broad checkpoint. | Stabilize content first, then choose an element-level snapshot where that matches the review question. Do not mask the entire page to suppress a local issue. |
| Intentional UI change keeps failing review | The old baseline remains the comparison target. | Review the change and approve the new baseline through the project’s visual review workflow; avoid updating baselines before inspecting the diff. |
| Local and cloud results disagree | Rendering environment, fonts, browser, viewport, or device scale differs. | Use the same environment for local comparisons or rely on the service’s documented rendering setup for the authoritative result. |
8. Performance, reliability, and cost
Every visual checkpoint adds capture and review work. Keep the suite focused on important states, avoid duplicating the same screen across many tests, and use stubs where a full backend journey is not needed for the visual question. End-to-end coverage still has value for critical user journeys, but Cypress notes that it can require more setup and maintenance and may need dedicated backend and CI infrastructure. Component tests offer a narrower surface for component appearance.
For reliability, treat the visual step as one part of the CI pipeline: protect tokens, make network inputs deterministic, retain enough run context to investigate a diff, and define who reviews and approves baselines. A visual snapshot does not replace the functional checks that prove the workflow succeeded. No single service choice removes the need to maintain stable tests.
For cost, compare the service’s current subscription terms with the engineering time and infrastructure needed to run local comparison and baseline review. Cypress Cloud pricing is distinct from Percy pricing. No fixed Percy price, plan limit, or service-level guarantee is stated here; consult the vendor for current terms and coverage.
9. Or skip the browser setup
If you need screenshots for documentation, monitoring, or a visual artifact and do not need Cypress to drive an application workflow, ScreenshotNeo offers a website screenshot API and MCP server. It is not a replacement for Cypress assertions or Percy baseline review; it is a direct screenshot option.
One GET request returns an image or PDF. The following example 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
Equivalent Python request:
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)
Equivalent Node.js request:
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 request failed: ${res.status}`);
await Bun.write('shot.webp', res);
See the ScreenshotNeo API documentation for authentication, response details, and options. ScreenshotNeo accepts the parameter names used by other screenshot APIs to make switching easier. Its 63 options include full-page capture with lazy images loaded, CSS-selector element capture, device and viewport settings, dark mode, retina scale, custom CSS and JavaScript, click-before-capture, hiding selectors, wait conditions, request and resource blocking, headers, cookies, user agent and authorization, timezone and geolocation, transparent backgrounds, resizing, cache TTL, signed public image links, async jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI spec. PDF settings include paper size, margins, landscape, and page ranges. The API also supports HTML/CSS-to-image output.
Before capture, it accepts the cookie or consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000, and all features are on every plan.
Sign up for 1,000 free screenshots a month with no card.
10. Frequently asked questions
Does Percy replace Cypress?
No. Cypress drives the test and checks behavior; Percy adds visual capture, comparison, and review.
Does a Percy snapshot validate accessibility?
No. A visual rendering does not prove semantic structure, keyboard behavior, or color contrast. Use accessibility checks alongside visual and functional tests.
Should every Cypress test take a visual snapshot?
No. Snapshot meaningful states where appearance is an explicit thing the team wants to review.
Can Cypress screenshots be used without Percy?
Yes. cy.screenshot() can save screenshots for debugging or artifacts. You still need a comparison system if you want automated baseline diffs and visual review.
Is Percy the only visual-testing integration for Cypress?
No. Cypress documents multiple integrations and open-source plugin options. Compare them against your rendering, review, ownership, and budget requirements.


