How to Use Percy for Visual Testing on Pull Requests
Add Percy snapshots to your tests, run them in pull request CI, and review visual changes against an approved baseline.
Percy adds visual snapshots to an existing test workflow. Add snapshots at stable, meaningful UI states, provide your Percy project token through an environment variable or CI secret, and run the instrumented tests from your pull request job. Percy compares each visual build with an approved baseline; a person decides whether each difference is expected.
This guide gives a Cypress example and a Storybook workflow based on Percy’s vendor tutorials. Package names, commands, and options can change, so confirm the current instructions for your framework and CI provider in Percy’s current documentation before adopting them. The Cypress command below is specific to that tutorial; it is not a universal Percy command.
1. Choose what the pull request should protect
Decide which visual states matter before adding snapshots. A useful snapshot captures a page or component after it has reached the stable, user-visible state your team wants to preserve. Examples include a key page after navigation or a component story that represents an important state.
- Prefer meaningful checkpoints over indiscriminate snapshots of every test step.
- Use consistent, descriptive snapshot names so reviewers can identify the page or component and state.
- Wait for the interface to settle before capturing it. Asynchronous content and animation can make comparisons noisy.
- Decide whether pull request coverage should focus on component stories, end-to-end journeys, or both.
There is no universal ideal snapshot count in the cited tutorials. Keep the set focused on important user-facing views and expand it when a missed change or a new risk gives you a reason.
2. Connect the run to a Percy project
- Create or select the Percy project for the application or component library.
- Make that project’s token available to local runs and CI through an environment variable.
- In CI, store the token using the provider’s secret mechanism. Do not commit it to the repository or print it in logs.
The exact secret configuration depends on your CI provider. The Cypress vendor tutorial demonstrates using an environment variable named PERCY_TOKEN; confirm the current variable name and setup in the integration documentation for your project.
3. Cypress: add snapshots and run the test command through Percy
The following is the Cypress integration pattern described in Percy’s vendor tutorial. Check current package versions and setup instructions before using it.
Install the tutorial’s packages
npm install --save-dev @percy/cli @percy/cypress
Wire the Cypress integration into the project as described by its current guide, then add a snapshot at a meaningful point in a test. For example:
describe('pricing page', () => {
it('shows the pricing options', () => {
cy.visit('/pricing');
cy.percySnapshot('Pricing page - default state');
});
});
This example assumes the Cypress integration is installed and configured and that /pricing is reachable in the test environment. Add any application-specific login, test data, or readiness steps before the snapshot so it consistently captures the intended state.
Run the instrumented Cypress tests
PERCY_TOKEN=your-project-token npx percy exec -- cypress run
The wrapper shown here is the Cypress example from the tutorial. In CI, inject the token from a secret rather than placing a real token in the command or checked-in configuration. For local shells, an environment variable can be set for the process; use your shell’s normal environment syntax and avoid sharing the token.
4. Storybook: capture component states
Percy’s vendor tutorial describes a Storybook flow: produce a static Storybook build, make the Percy token available, run Percy against the generated Storybook output, and put that command in CI. This is useful when the visual states you want to review are represented by stories. It does not replace end-to-end coverage when you also need to protect full user journeys.
The research available for this guide does not establish a current universal Storybook command or option set. Follow the current Percy Storybook integration instructions for the package names, build command, output directory, and Percy CLI invocation that match your versions.
5. Add Percy to pull request CI
Add the Percy-enabled test or Storybook command to the CI job that runs for pull requests. The job needs the application or Storybook environment, its usual test dependencies, and the Percy project token supplied securely. Exact workflow syntax depends on your CI provider, and the researched tutorials do not verify one shared configuration for every provider.
- Run the same relevant test setup the job already uses.
- Run the framework-specific Percy command so snapshots are collected and a visual build is produced.
- Make the resulting build available to the team for review as part of pull request review.
- Confirm in your provider and Percy project how the build is surfaced and how your team should treat its status before merging.
Do not assume a specific GitHub status-check name or merge-blocking behavior from this guide; those details were not verified by the available research. Configure required checks only after confirming the current Percy and CI documentation for your setup.
6. Review visual changes against the baseline
The first accepted visual state establishes the comparison point. Later builds show differences against that baseline. A difference tells you the rendered appearance changed; it does not by itself prove the change is a defect.
- Inspect each changed snapshot in the context of the pull request.
- Approve a change when it is an intentional design or content update, advancing the expected visual state.
- Fix an unintended regression, then rerun the relevant checks so reviewers can inspect the corrected result.
- Do not approve diffs mechanically. A passing functional test does not establish that the interface still looks correct.
7. Choose the coverage approach that fits the repository
| Approach | Useful when | Decision to make |
|---|---|---|
| Cypress snapshots | The repository already runs Cypress journeys and you want visual checkpoints in those tests. | Choose stable points in important user flows and confirm the current Cypress integration. |
| Storybook snapshots | Important visual states are represented as component stories. | Choose representative stories and confirm the current static build and Percy invocation. |
| Another framework | The existing suite uses a different test runner, such as Playwright or Selenium. | Use that framework’s current Percy integration guide. The research for this article does not verify current commands for those frameworks. |
Compare these choices by the states they capture, the framework the team already uses, how the command fits into pull request CI, and whether the snapshots represent stable states. The available research supports no performance or price comparison between these approaches.
8. Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| No Percy build appears | The test was run without the Percy wrapper, or the project token was absent or invalid. | Confirm the framework-specific command, token environment configuration, and current integration instructions. |
| The token works locally but not in CI | The CI job does not expose the configured secret to that run or branch context. | Check the CI provider’s secret settings and job environment. Do not paste the token into logs to diagnose it. |
| A Cypress snapshot call is unknown | The Cypress integration may not be installed or configured for the project, or package instructions may have changed. | Confirm the current package names and setup steps for the Cypress integration. |
| Snapshots differ on repeated runs | The page may be captured before it settles, or asynchronous content and animation may vary. | Capture after the intended stable state and review whether the selected state is deterministic. |
| A diff appears even though the change seems harmless | The rendered page changed, but a visual difference alone does not classify the change as a regression. | Inspect the snapshot in context; approve intentional updates or fix unintended changes. |
| Storybook capture fails or misses stories | The static build output, directory, or command may not match the current integration setup. | Check the current Storybook guide for build and output configuration rather than reusing a Cypress command. |
| A pull request does not show the expected check | CI and Percy status behavior may depend on current project and provider configuration. | Verify current integration settings and status behavior with the relevant documentation; do not infer a check name from this guide. |
9. Reliability, runtime, and cost considerations
Keep captures repeatable by naming snapshots clearly and waiting for the user-visible state to settle. Focus on important states so reviewers can understand each diff. The available sources do not provide measured runtime, reliability, or pricing comparisons, so estimate CI impact from your own workflow and consult current product documentation for service terms.
Keep credentials scoped to CI secrets, and avoid exposing them in committed files or logs. Treat visual review as a human decision in the pull request process: functional tests cover behavior, while snapshot review helps people judge appearance.
Or skip the browser setup
If you need a clean screenshot as an input to a workflow, ScreenshotNeo is a website screenshot API and MCP server. It is not a replacement for Percy’s baseline comparison and pull request review workflow; it can provide screenshots through one GET request, or let an MCP client request a capture. See the ScreenshotNeo API documentation for the request options.
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}`);
ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each 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 includes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a 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
Do I need to replace my functional tests to use Percy?
No. The documented workflow adds visual snapshot calls to selected points in an existing test or component workflow.
Does every visual difference mean the pull request is broken?
No. It is a change to review. A person decides whether it is intentional; approve expected changes or fix regressions.
Can I use the Cypress command for Playwright or Storybook?
Do not assume so. The command shown is the Cypress tutorial example. Use the current integration instructions for the framework and project you run.
How many snapshots should a pull request capture?
The researched material sets no universal count. Cover important, stable states and adjust based on the risks your team needs to review.


