Engineering3 min read

Visual Regression Testing

Catch unintended UI changes with visual regression tests - Chromatic, Percy, Playwright screenshot comparison, and integrating visual tests in CI.

Visual TestingChromaticPercyStorybookScreenshot Testing

skill: Visual Regression Testing category: Engineering tags: [Visual Testing, Chromatic, Percy, Storybook, Screenshot Testing] readingTime: 3

Visual regression testing catches the bugs that automated functional tests miss - the button that's technically clickable but has 0px height, the modal that appears behind other content, the text that overflows its container on a slightly different viewport. Pixel comparison is the only way to catch these reliably.

Workflow

  1. Set up Chromatic with StorybookChromatic is the easiest visual regression solution for Storybook - it captures screenshots of every component story, compares them to the approved baseline, and blocks PRs with unreviewed changes.
  2. Use Playwright for page-level screenshotsChromatic tests individual components; Playwright screenshot tests verify entire page layouts, including cross-component interactions and responsive behavior at different viewports.
  3. Manage baseline approvals and driftVisual regression tests require an approved baseline. Without a workflow for baseline updates, tests become noisy (too many false positives) and are disabled.
  4. Integrate visual tests in CI gateVisual tests in CI block PRs with regressions. The key is balancing sensitivity (catch real bugs) with noise (don't block PRs on rendering differences).
  5. Optimize for speed and reliabilityVisual tests are slower than functional tests. Parallelization, scoping to changed components, and intelligent baseline management keep visual tests under 5 minutes in CI.

Output Format

Visual regression: Chromatic with Storybook for components + Playwright screenshots for pages, maxDiffPixelRatio:0.01 tolerance, mask dynamic content, always run on same Docker image for consistency, and artifact upload on failure.

Chromatic: chromaui/action + CHROMATIC_PROJECT_TOKEN + exitOnceUploaded
Playwright: expect(page).toHaveScreenshot("name.png", { maxDiffPixelRatio:0.01, animations:"disabled" })
Mask: { mask: [page.locator(".timestamp")] } for dynamic content
Update: npx playwright test --update-snapshots -- --grep "component"
CI: always ubuntu container image for cross-machine consistency

Examples

Input

Output

Edge Cases & Gotchas

  • Flaky screenshots from animations - CSS animations capture at different frame positions each run. Always set animations: "disabled" in Playwright screenshot options. For Lottie or Canvas animations, wait for a stable frame before capture.
  • Font rendering differences - System fonts render differently across OS versions. Use web fonts (loaded from same URL) or embed fonts in your test container to ensure consistent rendering.
  • Baseline update workflow - When multiple developers update baselines independently, git merge conflicts occur on screenshot binary files. Establish a convention: one PR per visual change, baseline updates in separate commits.
  • Testing dark mode visually - Test both light and dark mode: await page.emulateMedia({ colorScheme: "dark" }) before taking screenshots. Maintain separate baseline sets for each theme.

When to Break These Rules

  • Skip visual regression for B2B dashboards where data varies - masking all data effectively tests nothing but the layout chrome. Use component-level Chromatic tests for design system instead.
  • Skip visual regression early in a project when the UI is changing rapidly - the overhead of updating baselines on every PR slows development. Add visual tests when the design stabilizes.

Reference Files

  • https://www.chromatic.com/docs/
  • https://playwright.dev/docs/screenshots

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.