Visual Regression Testing
What it is
Section titled “What it is”A visual regression test captures a rendered UI as an image and compares it to a baseline from a previous build. Any unexpected pixel difference — a shifted button, a broken font, a CSS leak — is flagged for review.
Some tools compare pixels directly; others (Applitools) model the page like a human eye and use AI diffing. Either way the workflow is the same: capture → compare → review → accept or reject.
baseline.png new render │ │ └────────┬───────────┘ ▼ diff engine │ ┌────────┴─────────┐ ▼ ▼ within tolerance beyond threshold (pass) (fail → review diff)Visual tests complement functional tests: a green E2E run can still ship a layout that is visually broken.
When to use it / when to avoid it
Section titled “When to use it / when to avoid it”Use it for
- Design systems and shared component libraries
- Responsive layouts across breakpoints and browsers
- Marketing pages, themes, dark mode, localisation where layout is the product
- Catching CSS regressions that DOM assertions cannot see
Avoid it when
- Content changes constantly (news feeds, live data) — use masking or skip
- There is no stable rendering environment, so results flake on antialiasing
- You test every page pixel-by-pixel instead of a small, curated set
- You have no review workflow, so diffs pile up and get rubber-stamped
Frameworks & tools
Section titled “Frameworks & tools”| Tool | Ecosystem | Notes |
|---|---|---|
Playwright toHaveScreenshot |
JS/TS | Built in; no account required; runs locally and in CI |
| Chromatic | JS/TS | Storybook-native, cloud review UI, TurboSnap |
| Percy | JS/TS, multi | Cloud baselines, cross-browser rendering |
| Applitools Eyes | Multi | AI visual diffing, cross-device viewports |
| BackstopJS | JS/TS | Open-source, Puppeteer/Playwright based, local baselines |
| reg-suit | JS/TS | OSS CLI, stores baselines in your own storage |
| Loki | JS/TS | Storybook screenshot testing with Chromatic-like review |
Quick setup
Section titled “Quick setup”npm install -D @playwright/testnpx playwright install --with-depsnpx playwright test --update-snapshots # create baselinesnpx playwright test # comparenpm install -D chromaticnpx chromatic --project-token=$CHROMATIC_TOKENpip install pytest-playwrightplaywright installpytest --update-snapshots # regenerate baselinesgo get github.com/playwright-community/playwright-go# or drive BackstopJS / Percy from a Node stepdotnet add package Microsoft.Playwright.NUnitpwsh bin/Debug/net8.0/playwright.ps1 installExample
Section titled “Example”import { test, expect } from '@playwright/test';
test('pricing page looks correct', async ({ page }) => { await page.goto('/pricing'); await expect(page).toHaveScreenshot('pricing.png', { maxDiffPixelRatio: 0.01, mask: [page.getByTestId('live-counter')], });});def test_pricing_page(page): page.goto("/pricing") expect(page).to_have_screenshot("pricing.png", max_diff_pixel_ratio=0.01)func TestPricingPage(t *testing.T) { // pseudocode: run in an isolated browser context page.Goto("/pricing") // capture and compare pricing.png against the baseline}@Testvoid pricingPage() { page.navigate("/pricing"); assertThat(page).matchesSnapshot("pricing.png");}[Test]public async Task PricingPage(){ await Page.GotoAsync("/pricing"); await Expect(Page).ToHaveScreenshotAsync("pricing.png");}Anti-patterns & best practices
Section titled “Anti-patterns & best practices”- Pin the rendering environment. Same OS, browser version, fonts and device scale factor across baseline and CI, or every diff is noise.
- Mask or freeze dynamic regions. Clocks, avatars, ads and live counters should be masked so only real changes surface.
- Test a curated set, not every page. Focus on shared components and high-traffic layouts; a giant matrix just multiplies flaky diffs.
- Always review diffs, never auto-accept. A visual review gate is the whole point; blanket approval defeats it.
- Set an explicit tolerance.
maxDiffPixelRatio/thresholds absorb antialiasing noise while still catching real layout breaks. - Run visual tests in CI on a fixed viewport list. Reproduce results headlessly and deterministically, not on a developer laptop.
Self-assessment quiz
Section titled “Self-assessment quiz”Self-assessment quiz
Pick one answer per question, then press Check answers.