Percy Visual Testing: Cloud Screenshot Comparison for Web Teams
How Percy Works
Percy's workflow differs from local screenshot tools in a key way: instead of capturing pixel screenshots on your machine, Percy captures the serialized DOM along with all CSS, fonts, and assets at the moment of the snapshot. This serialized state is uploaded to Percy's cloud, where it renders the page in real browser engines at your configured viewport widths and browser combinations. The cloud rendering ensures consistent screenshots regardless of the developer's local operating system or font configuration.
The comparison uses a perceptual diff algorithm that goes beyond raw pixel comparison. It accounts for anti-aliasing differences, sub-pixel rendering variations between browser engines, and minor rendering nonconsistencies that produce meaningless pixel diffs. This filtering reduces false positive rates compared to pure pixel comparison tools like BackstopJS, though it does not eliminate them entirely the way AI-powered tools like Applitools aim to.
Each set of snapshots from a test run constitutes a "build" in Percy. Builds are linked to commits and pull requests through Git integration, and Percy posts a status check to your pull request showing how many snapshots changed. The PR cannot merge until a reviewer approves or dismisses the visual changes in the Percy dashboard.
Integrating Percy with Test Frameworks
Percy provides official SDKs for every major test framework. Integration typically means installing an SDK package and adding one function call per snapshot.
Percy with Playwright
Install the SDK:
npm install --save-dev @percy/cli @percy/playwright
Add snapshot calls to your Playwright tests:
import { test } from '@playwright/test';
import percySnapshot from '@percy/playwright';
test('homepage visual test', async ({ page }) => {
await page.goto('https://your-site.com');
await percySnapshot(page, 'Homepage');
});
Run through the Percy CLI:
npx percy exec -- npx playwright test
The Percy CLI wrapper collects all snapshots from the test run and uploads them as a single build.
Percy with Cypress
Install:
npm install --save-dev @percy/cli @percy/cypress
Import in your Cypress support file and use in tests:
import '@percy/cypress';
it('pricing page', () => {
cy.visit('/pricing');
cy.percySnapshot('Pricing Page');
});
Run: npx percy exec -- npx cypress run
Percy with Storybook
For component-level visual testing, Percy integrates with Storybook without modifying any stories:
npm install --save-dev @percy/cli @percy/storybook
npx percy storybook https://your-storybook-url.com
Percy crawls every story in your storybook, captures snapshots of each, and runs comparison against baselines. This provides visual coverage of every component state you have documented as a story, with zero test code required.
The Review Dashboard
Percy's review dashboard is its strongest differentiator from self-hosted tools. Each build shows a list of all snapshots that changed compared to the baseline build. For each changed snapshot, the dashboard offers three views:
- Side-by-side: baseline and new screenshot next to each other at full resolution
- Diff overlay: changed pixels highlighted in a configurable color over the baseline or new image
- Focus mode: zoomed view of just the changed regions for quick review of small differences
Reviewers click "Approve" on individual snapshots or approve the entire build. Approved snapshots become the new baselines for subsequent builds. If a reviewer spots an unintended change, they leave a comment or reject the build, which blocks the pull request from merging.
The dashboard supports team workflows: multiple reviewers can be assigned to a build, notifications go to configured Slack channels or email, and approved baselines are visible to every team member. This is particularly valuable when designers participate in visual review, since they can evaluate changes in the dashboard without needing Git access or a local development environment.
Responsive and Cross-Browser Testing
Percy handles responsive testing through width configuration rather than viewport configuration. You specify the widths you want to test, and Percy renders each snapshot at each width:
// In percy config or inline
await percySnapshot(page, 'Homepage', {
widths: [375, 768, 1280],
});
Each width counts as a separate snapshot for billing purposes. Three widths means three snapshots per percySnapshot() call.
Cross-browser rendering is available on paid plans. Percy can render snapshots in Chrome and Firefox, with each browser counting as a separate snapshot. The cloud rendering ensures consistency across browsers without maintaining browser installations locally.
For responsive visual testing beyond viewport width, such as testing touch interactions or mobile-specific behaviors, Percy's width-based approach captures layout and styling differences but does not simulate mobile device characteristics like touch targets or mobile Chrome rendering quirks.
Pricing and Free Tier
Percy prices by snapshot volume. A snapshot is one rendered screenshot at one width in one browser. The math matters because it multiplies quickly:
- 50 pages x 3 widths x 1 browser = 150 snapshots per build
- 150 snapshots x 20 builds per month = 3,000 snapshots per month
- Add a second browser: 6,000 snapshots per month
The free tier includes 5,000 snapshots per month, which covers small projects running moderate CI frequency. Paid plans start at several hundred dollars per month for 25,000 snapshots, with higher tiers for larger volumes. Enterprise plans add features like SSO, audit logs, and dedicated support.
Cost optimization strategies: limit widths to 2 or 3 meaningful breakpoints rather than testing many sizes, test only changed pages when possible (Percy's SDK supports this through selective snapshot calls), and use Percy for integration testing while handling component-level visual testing separately with a tool like Chromatic or Playwright screenshots.
Handling Dynamic Content in Percy
Dynamic content is the leading source of visual testing noise, and Percy provides several mechanisms to handle it. Since Percy captures the DOM rather than a raw screenshot, you can manipulate the DOM before the snapshot call to stabilize the content.
Hide or remove elements with the percyCSS option, which injects CSS into the page before rendering:
cy.percySnapshot('Dashboard', {
percyCSS: `
.live-timestamp { visibility: hidden; }
.user-avatar { opacity: 0; }
.animated-banner { display: none; }
`,
});
This CSS applies only during Percy's rendering, not during your test execution, so it does not interfere with functional test assertions that depend on those elements. It is cleaner than modifying the DOM in your test code because the manipulation is scoped to Percy only.
Percy also supports a scope option to capture only a specific portion of the page, which is useful when only part of a page has stable content worth testing:
await percySnapshot(page, 'Product Card', {
scope: '.product-card-container',
});
For pages with heavy animation, Percy automatically waits for network idle and DOM stability before capturing, but you can add explicit waits in your test code if the default stabilization is not sufficient. Percy processes snapshots asynchronously in the cloud, so adding a wait before the snapshot call has no impact on Percy's rendering pipeline, only on when the DOM state is captured.
Date and time content is a particularly common dynamic element. Rather than hiding timestamps, consider replacing them with fixed values before the snapshot. In Playwright, you can use page.evaluate() to set all date elements to a fixed string. In Cypress, use cy.clock() to freeze the JavaScript clock at a known time, which makes all date-rendering code produce the same output on every run.
Percy vs Alternatives
Percy vs BackstopJS: Percy handles rendering infrastructure, baseline management, and review workflows in the cloud. BackstopJS runs locally with zero cost. Choose Percy when you want a managed service with a team review dashboard. Choose BackstopJS when you want zero cost and full control.
Percy vs Chromatic: Chromatic is purpose-built for Storybook and excels at component-level visual testing. Percy integrates with everything. If your project lives in Storybook, Chromatic is the better fit. If you need page-level testing or use multiple test frameworks, Percy is more versatile.
Percy vs Applitools: Applitools uses AI-powered comparison that nearly eliminates false positives. Percy uses perceptual diff that is better than raw pixel comparison but less sophisticated than AI. Applitools costs more. For large test suites where false positive fatigue is the primary problem, Applitools may have better ROI. For most teams, Percy's comparison is accurate enough.
Percy vs Playwright built-in: Playwright screenshots are free, require no external service, and integrate tightly with your E2E tests. Percy adds cross-browser rendering, a review dashboard, and team collaboration features. Start with Playwright built-in, upgrade to Percy if you outgrow file-based baseline management or need cross-browser rendering without maintaining the infrastructure yourself.
Getting Started with Percy
The fastest setup path is: create a Percy project at percy.io, copy your project token, set it as the PERCY_TOKEN environment variable, install the SDK for your test framework, add percySnapshot() calls to your most important tests, and run through the Percy CLI. The first build creates baselines, and every subsequent build compares against them.
Start with your 5 to 10 highest-value pages at 2 to 3 viewport widths. Expand coverage as the workflow matures and the team gets comfortable with the review process. The goal is building trust in the system before expanding scope, because a large visual test suite that the team does not trust is worse than a small one that the team relies on.
Percy provides the most complete managed visual testing experience: cloud rendering, perceptual comparison, a review dashboard, and tight Git integration. The cost scales with snapshot volume, so plan your viewport and browser strategy carefully. It is the right choice for teams that want visual testing without building the infrastructure themselves.