Skip to main content
Back to Blog
Comparison
2026-08-28

Percy vs Applitools vs Chromatic: Visual Regression Tools Compared

Percy vs Applitools vs Chromatic visual regression comparison for QA teams choosing screenshot, AI, or Storybook review workflows before release.

Percy vs Applitools vs Chromatic: Visual Regression Tools Compared

Percy vs Applitools vs Chromatic is a choice between three visual regression workflows: Percy focuses on screenshot diffs for web apps, Applitools focuses on AI-assisted visual validation across apps and browsers, and Chromatic focuses on Storybook component review plus visual regression. Pick based on what you need to protect: deployed pages, cross-platform user journeys, or component states.

For QA engineers, the best tool is the one that catches meaningful visual regressions without turning every spacing change into an approval meeting. That depends on baseline ownership, test entry point, component coverage, browser needs, and how your developers review changes.

The Short Decision

If your team already uses Storybook heavily, Chromatic is usually the fastest path to useful component-level visual regression. If you need broad app-page screenshot coverage tied to CI and you are already in the BrowserStack ecosystem, Percy is straightforward. If you need advanced visual matching across complex journeys, dynamic regions, PDFs, mobile, or enterprise review workflows, Applitools deserves a serious trial.

That is the practical answer. The deeper answer is that these tools are not exact substitutes. They overlap on screenshots and baselines, but they enter the product at different layers.

ToolBest entry pointStrongest fitWatch-outs
PercyBrowser automation and snapshotsWeb page regression in CINeeds disciplined snapshot scope
ApplitoolsSDK checkpoints during testsComplex visual validation across surfacesMore configuration choices to govern
ChromaticStorybook storiesComponent state review and design systemsWeak if Storybook coverage is thin

Official docs are the right place for current SDK details: https://www.browserstack.com/docs/percy, https://applitools.com/docs/, and https://www.chromatic.com/docs/. Tool vendors change package names, examples, and plan details over time, so pin your implementation to the docs and your lockfile, not a blog snippet alone.

For focused setup detail, use a Percy visual testing complete guide when Percy is the candidate, and a Chromatic Storybook visual testing guide when component review is the main target.

What Each Tool Is Really Optimizing For

Visual regression tools all compare images, but the workflow around the image is the product. Percy wants you to take snapshots at meaningful points during browser tests or static builds. Applitools wants you to define visual checkpoints and let its matching system separate important differences from noise. Chromatic wants Storybook stories to become the visual source of truth for component states.

Workflow questionPercyApplitoolsChromatic
Where do snapshots come from?Web app tests or static pagesTest checkpoints across supported SDKsStorybook stories
Who reviews changes?Developers and QA in Percy UIDevelopers, QA, design, release teamsDevelopers and designers around Storybook
What is the natural unit?Page or selected regionCheckpoint, window, page, component, app stateStory
What does it reward?Clear snapshot naming and stable statesIntentional match levels and ignore regionsComplete story coverage
What makes it fail badly?Too many broad snapshotsPoorly governed match settingsStories that do not represent production states

The wrong way to evaluate these tools is to run the home page once in each and compare screenshots. That tests almost nothing. Use a real flow with dynamic data, a loading state, a responsive breakpoint, and one deliberate visual bug. Then see which tool gives the cleanest signal and review path.

Coverage Layer: Pages, Flows, or Components

Start with the layer you are most afraid of breaking. Marketing pages, dashboards, and checkout flows benefit from page-level screenshots. Design systems and shared components benefit from Storybook states. Regulated or high-value flows may need both component and page coverage because a component can pass alone and fail inside real layout.

Coverage targetBetter first choiceReason
Storybook design systemChromaticStories are already the coverage surface
Checkout and account flowsPercy or ApplitoolsBrowser flow state matters
Dynamic enterprise dashboardApplitoolsIgnore regions and smarter matching may reduce noise
Static content pagesPercySimple snapshots are often enough
Component library with design reviewChromaticReview fits pull-request component changes

A Playwright screenshot baseline with no service can still catch local changes, but hosted visual tools solve review, storage, baseline approvals, collaboration, and CI summaries. That is what you are paying for.

Here is a plain Playwright screenshot test that shows the minimum concept before a vendor SDK enters:

import { test, expect } from '@playwright/test';

test('checkout summary stays visually stable', async ({ page }) => {
  await page.goto('/checkout/summary?fixture=visual');
  await expect(page.getByTestId('checkout-summary')).toHaveScreenshot(
    'checkout-summary.png',
    {
      animations: 'disabled',
      caret: 'hide'
    }
  );
});

That works for small teams, but it becomes painful when designers need to review baselines or when multiple branches touch the same surfaces. Percy, Applitools, and Chromatic earn their keep when visual review becomes a shared workflow, not just a test assertion.

Percy: Strengths, Weaknesses, and Test Shape

Percy is a good fit when you want page and region snapshots from browser tests without designing a large visual AI program. It is especially practical for QA teams already running Playwright, Cypress, Selenium, or WebdriverIO against stable test data.

A good Percy suite names snapshots by product meaning, not route alone. "Billing settings with tax ID error" is better than "settings page." The reviewer should know what changed before opening the diff.

import { test } from '@playwright/test';
import percySnapshot from '@percy/playwright';

test('billing settings visual states', async ({ page }) => {
  await page.goto('/settings/billing?fixture=visual');
  await percySnapshot(page, 'Billing settings - default');

  await page.getByRole('button', { name: 'Add tax ID' }).click();
  await page.getByLabel('Tax ID').fill('bad-value');
  await page.getByRole('button', { name: 'Save tax ID' }).click();

  await percySnapshot(page, 'Billing settings - tax ID error');
});

Percy works best when you freeze volatile data. Dates, avatars, random recommendations, ads, third-party widgets, and live charts can turn every run into noise. You can hide or stabilize dynamic regions in the app, route fixtures, or test setup. The cleanest approach is usually deterministic test data, because it keeps the screenshot honest.

Percy's weak spot is not that it lacks value. It is that teams take too many broad page snapshots and drown reviewers. Be selective. Snapshot states that matter: permission dialogs, empty states, form errors, responsive navigation, checkout totals, data-heavy tables, and layout boundaries that developers frequently break.

Applitools: Strengths, Weaknesses, and Test Shape

Applitools is strongest when the visual question is more nuanced than pixel equality. Its SDKs and matching modes are designed for teams that need to compare app states while tolerating expected rendering differences. That can be valuable in cross-browser testing, enterprise apps with dynamic regions, and suites that include more than simple web pages.

A typical test places visual checkpoints inside an existing functional flow:

import { test } from '@playwright/test';
import { Eyes, Target } from '@applitools/eyes-playwright';

test('account overview visual checkpoint', async ({ page }) => {
  const eyes = new Eyes();

  await eyes.open(page, 'Customer Portal', 'Account overview');

  await page.goto('/account?fixture=visual');
  await eyes.check('Overview loaded', Target.window().fully());

  await page.getByRole('tab', { name: 'Invoices' }).click();
  await eyes.check('Invoices tab', Target.window().fully());

  await eyes.close();
});

The exact setup depends on the current SDK docs and your runner. The important pattern is stable: open a visual session, drive the app to meaningful states, checkpoint those states, then close the session so results are reported.

Applitools gives teams more control, which means more governance. Match levels, ignore regions, layout regions, branch baselines, and batch naming should be documented. Without rules, one team turns matching strict and blocks harmless antialiasing, while another ignores entire panels and misses real bugs.

Use Applitools when visual validation is a first-class QA discipline, not just a sidecar. If all you need is a few page screenshots on PRs, the extra power may be more than you want to operate.

Chromatic: Strengths, Weaknesses, and Test Shape

Chromatic is different because Storybook is the center. That is excellent when components are the product surface: design systems, shared UI packages, marketing blocks, checkout widgets, and reusable forms. It is weaker when the bug only appears after routing, real data loading, auth, or several user actions.

Chromatic rewards complete stories. If a button has default, hover-like, loading, disabled, destructive, and long-label states, those states need stories. If a data table has empty, loading, error, one row, many rows, and overflow states, those need stories too.

import type { Meta, StoryObj } from '@storybook/react';
import { InvoiceTable } from './InvoiceTable';

const meta = {
  component: InvoiceTable,
  title: 'Billing/InvoiceTable'
} satisfies Meta<typeof InvoiceTable>;

export default meta;

type Story = StoryObj<typeof meta>;

export const WithOverdueInvoice: Story = {
  args: {
    invoices: [
      {
        id: 'inv-101',
        customer: 'ACME Ltd',
        amount: '$320.00',
        status: 'Overdue'
      }
    ]
  }
};

export const Empty: Story = {
  args: {
    invoices: []
  }
};

The CI command is simple once the project is connected, but exact tokens and project setup belong in Chromatic's current docs.

name: chromatic

on:
  pull_request:

jobs:
  visual:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm run build-storybook
      - run: npx chromatic --project-token=${{ secrets.CHROMATIC_PROJECT_TOKEN }}

Chromatic's biggest weakness is inherited from Storybook coverage. If developers only write happy-path stories, your visual suite only protects happy paths. QA should review the story inventory the same way they review test cases.

Baseline Governance and Review Workflows

Visual regression fails when nobody owns baseline changes. A baseline is a claim that "this is now acceptable." Treat it with the same seriousness as approving a test expectation.

Governance ruleWhy it matters
Name snapshots by user-visible stateReviewers can judge intent
Require design review for shared componentsQA should not approve design system changes alone
Keep dynamic data deterministicReduces false diffs
Use ignore regions sparinglyPrevents hiding real regressions
Track accepted diffsAccepted changes are product decisions

Percy and Chromatic often fit PR review loops where developers approve expected changes and designers review sensitive components. Applitools often appears in more formal QA flows with batches, environments, and cross-browser checkpoints. Those are tendencies, not laws.

One practical policy: if a diff changes layout, spacing, color, typography, or hierarchy in a shared component, the code reviewer should not approve the baseline alone unless they own design quality. If a diff changes dynamic content that should have been frozen, reject the baseline and fix the fixture.

Dynamic Data and False Positives

False positives are not harmless. They train teams to rubber-stamp visual diffs. The best visual suites aggressively remove randomness and only ignore areas that cannot be made deterministic.

Use this priority order:

ProblemPreferred fixLast resort
Current date changesInject fixed test clockIgnore tiny date region
User avatars changeUse fixture avatarsMask avatar region
Chart animation differsDisable animation or use fixed dataSnapshot final stable state
Ads or third-party widgetsDisable in test envMask widget area
Font rendering differsPin environment and fontsUse less strict matching if tool supports it

A visual helper can wait for app readiness before snapshotting:

import type { Page } from '@playwright/test';

export async function prepareVisualState(page: Page) {
  await page.addStyleTag({
    content: [
      '* { animation-duration: 0s !important; }',
      '* { transition-duration: 0s !important; }',
      '[data-visual-ignore="true"] { visibility: hidden !important; }'
    ].join('\n')
  });

  await page.waitForLoadState('networkidle');
  await page.getByTestId('app-ready').waitFor();
}

That helper does not fake the product. It removes timing noise and waits for a deliberate readiness signal. If the app never becomes ready, the test should fail. A screenshot of half-loaded UI is usually not useful evidence.

AI Matching Versus Pixel Diff

Pixel diffs are easy to understand. AI-assisted matching can reduce noise and catch layout issues that raw pixels express poorly. The tradeoff is explainability and configuration discipline. QA engineers should be comfortable asking, "What exact difference will this setting ignore?"

DifferencePixel diff behaviorAI-assisted matching behavior
One-pixel font smoothingOften noisyOften tolerated depending on settings
Button shifted below foldClear diffClear layout difference
Text changed but same shapeMay be subtleDepends on content and match mode
Dynamic table rowsVery noisyCan be managed with regions or layout matching
Missing iconClear if visibleClear if checkpoint covers it

My opinion: use stricter checks for critical transactional UI and looser layout-aware checks for data-heavy screens where content changes constantly. Do not make the whole suite permissive because one dashboard is noisy. That is how a missing submit button becomes an "accepted visual variance."

A Failure Story: The Approved Diff That Was a Bug

Symptom: a product card lost its price label in production. The visual test had caught a diff, but it was approved during a busy release week.

Wrong theory: the team blamed the visual tool for being noisy. They said nobody could inspect hundreds of diffs, so the process was doomed.

Actual cause: the suite snapshotted the entire catalog page with live product data. Every run changed badges, images, positions, and prices. Reviewers had been trained to approve noise. The missing price was one small change inside a wall of unrelated diffs.

Fix: QA replaced the broad live-data page snapshot with fixture-backed product card stories, one catalog page with deterministic data, and a policy that rejected baseline updates containing unrelated data churn. The next real price-label regression produced one focused diff and was fixed before merge.

The lesson is uncomfortable: a noisy visual suite is not neutral. It actively weakens review judgment.

Evaluation Plan Before You Buy

Run a two-week proof of concept against the same targets in each tool. Do not evaluate with toy pages. Use one stable component set, one dynamic dashboard, one critical flow, and one responsive layout.

Evaluation itemWhat to measure
Setup timeHours to first useful CI result
Signal qualityReal bugs caught versus false diffs
Review speedTime to approve expected changes
Developer fitWhether failures are understood in PRs
Design fitWhether designers can review the right surfaces
MaintenanceHow often snapshots need fixture updates

Seed one deliberate bug in each target:

.checkout-total {
  display: none;
}

.primary-action {
  background: #6b7280;
}

.invoice-table th {
  text-align: center;
}

Then ask three people to review the results: a QA engineer, a frontend developer, and a designer. The best tool is the one that produces the least argument about whether the failure matters.

CI Integration and Failure Ownership

Visual regression tools work best when CI failure policy matches the maturity of the suite. A brand-new visual suite should usually report changes without blocking every merge on day one. After fixtures are stable and ownership is clear, critical snapshots can become required checks. Blocking too early creates resentment. Never blocking creates wallpaper.

Use staged enforcement:

MaturityCI behaviorOwner action
PilotNon-blocking reportTune fixtures and snapshot scope
StabilizingBlock only critical surfacesAssign reviewers for diffs
MatureBlock protected visual suitesTrack noise and stale baselines
DegradedTemporarily narrow scopeFix causes before broadening again

Percy and Chromatic often plug into pull requests as visual checks. Applitools can also integrate into CI, with results grouped into batches. The important policy is the same: a failed visual run needs an owner who can decide whether the difference is expected, a product bug, or test noise.

Here is a generic GitHub Actions shape that keeps functional tests and visual snapshots separate. The exact vendor command belongs to the tool you choose, but the split is useful across all three.

name: visual-regression

on:
  pull_request:

jobs:
  functional:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm test

  visual:
    needs: functional
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm run test:visual

Separating the jobs makes ownership clear. A unit failure is not a visual baseline decision. A visual failure should carry screenshot evidence and a link to the review UI, not force developers to dig through generic logs.

Security and Privacy in Screenshots

Visual testing captures the UI, which means it can capture secrets, customer data, internal names, email addresses, tokens rendered by mistake, and private messages. QA teams should treat screenshots and traces as sensitive artifacts unless proven otherwise.

Create screenshot-safe fixtures:

Sensitive dataSafer fixture
Real customer namesFictional company names
Personal email addressesReserved test-domain addresses
Payment detailsProvider-approved test values
Access tokensNever render in UI
Private messagesSynthetic text with no real user content

Masking regions can help, but it should not be the first fix for sensitive data. If the app displays production-like private data in test, your risk is bigger than the visual tool. Use seed data that is safe to show in PR comments, vendor dashboards, and artifacts.

Also check retention. A visual diff from a pull request can outlive the branch. That is useful for audit and debugging, but risky if screenshots contain private data. Before adopting any tool, ask where images are stored, who can view them, how long they remain, and how access is revoked for former contractors.

Component Coverage Inventory

For Chromatic, the coverage inventory is the product. For Percy and Applitools, it is still useful because page snapshots often miss component states that only appear under specific data. QA can maintain a small inventory that maps user risk to visual coverage.

Component or surfaceRequired statesCoverage location
Primary buttonDefault, disabled, loading, destructiveStorybook
Invoice tableEmpty, loading, overdue, overflowStorybook and billing page
Checkout summaryDiscount, tax error, long addressBrowser flow
Navigation shellMobile menu, collapsed sidebar, active itemStorybook and app page
Alert bannerInfo, warning, error, long textStorybook

This inventory prevents a common argument: developers say "visual tests cover that," while QA means page screenshots and design means component states. Name the state and the location. If a state has no story and no page checkpoint, it is uncovered.

A simple story-generation discipline helps:

import type { Meta, StoryObj } from '@storybook/react';
import { AlertBanner } from './AlertBanner';

const meta = {
  component: AlertBanner,
  title: 'Feedback/AlertBanner'
} satisfies Meta<typeof AlertBanner>;

export default meta;

type Story = StoryObj<typeof meta>;

export const LongError: Story = {
  args: {
    tone: 'error',
    message: 'Payment authorization failed because the billing address needs review.'
  }
};

Long text stories are underrated. They catch wrapping, overflow, clipped icons, and button crowding before a real localized string breaks the page.

What People Get Wrong in Tool Bakeoffs

The most common mistake is comparing default screenshots instead of comparing operating cost. Any of the three tools can catch an obvious CSS bug in a stable demo. The question is which one your team will still maintain six months later.

During a bakeoff, measure these chores:

ChoreWhy it predicts success
Adding a new meaningful stateShows whether coverage will grow
Reviewing a noisy diffShows whether reviewers can separate signal from churn
Updating an expected baselineShows approval clarity
Debugging a CI-only diffShows artifact and environment quality
Removing obsolete snapshotsShows maintenance friction

Do not let the vendor demo drive the test. Bring your own ugly states: real dashboards, long names, feature flags, missing images, failed payments, loading skeletons, and mobile navigation. A visual tool that looks great on a polished landing page may struggle with the dense operational UI where your regressions actually happen.

My practical bias: start narrower than your ambition. Protect the ten surfaces that would embarrass the team if they broke. Make those reliable. Then expand. A small visual suite that everyone trusts beats a huge suite that everyone clicks through without reading.

Frequently Asked Questions

Is Percy better than Applitools for visual regression testing?

Percy is often better for straightforward web screenshot regression in CI, especially when teams already run browser tests and want a clean hosted review workflow. Applitools is often better when matching needs are more advanced, cross-browser differences are noisy, or the organization wants richer visual validation controls. The right answer depends on screenshot scope, dynamic content, review ownership, and how much configuration your team can govern well.

When should I choose Chromatic instead of Percy or Applitools?

Choose Chromatic when Storybook is already a serious part of development and component states are the main surface you need to protect. It is excellent for design systems and reusable UI packages. It is not a replacement for full-flow visual checks when bugs depend on routing, auth, API data, or several user actions. Many teams use Chromatic for components and another tool for critical pages.

Do visual regression tools replace functional end-to-end tests?

No. Visual regression tools can catch layout, styling, and content presentation changes, but they do not prove business logic by themselves. A checkout screenshot might show the right total, but a functional test should still assert the API call, order creation, and error handling. The strongest suites combine functional assertions with targeted visual checkpoints at states users actually care about.

How do I reduce false positives in visual tests?

Start with deterministic data, fixed clocks, disabled animations, stable fonts, and deliberate app-ready signals. Use ignore or mask regions only when the content cannot reasonably be stabilized. Keep snapshots focused on meaningful states instead of capturing every page at full width. Most false-positive problems come from broad screenshots of live data, not from the visual tool itself or its reviewer UI.