Visual Regression Testing
A complete guide to setting up visual regression testing for your project with UI Inspect.
Visual regression testing automatically detects unintended visual changes in your UI by comparing screenshots against approved baselines.
Why visual regression testing?
Unit tests verify logic. Integration tests verify behavior. But neither catches visual bugs:
- A CSS change that breaks layout on mobile
- A dependency update that changes button styles
- A z-index conflict that hides content
- Font loading failures that change typography
Visual regression testing fills this gap by comparing what the user actually sees.
Setting up your first test suite
Define what to test
Start with your most critical pages:
screenshots:
- name: "homepage"
url: "/"
viewports:
- { name: "desktop", width: 1920, height: 1080 }
- { name: "mobile", width: 375, height: 667 }
- name: "login"
url: "/login"
viewports:
- { name: "desktop", width: 1920, height: 1080 }
- name: "dashboard"
url: "/dashboard"
viewports:
- { name: "desktop", width: 1440, height: 900 }
- { name: "tablet", width: 768, height: 1024 }Establish baselines
Run your first build to capture initial screenshots:
npx ui-inspect build --project proj_id --auto-approveThe --auto-approve flag automatically sets these as baselines since there's nothing to compare against yet.
Integrate into CI
Add to your CI pipeline so every PR gets checked:
npx ui-inspect build --project proj_id --fail-on-diff --report --pr $PR_NUMBERReview and approve
When diffs are found:
- Check the PR comment or dashboard for visual diffs
- If intentional, approve the new baselines:
npx ui-inspect approve - If unintentional, fix the regression and push again
Best practices
Choose the right threshold
# Strict — for design system components
threshold: 0.05
# Standard — for most pages
threshold: 0.1
# Lenient — for pages with dynamic content
threshold: 0.2Handle dynamic content
Use ignore regions for timestamps, ads, or user-generated content:
await client.comparison.updateIgnoreRegions.mutate({
id: comparisonId,
regions: [
{ x: 10, y: 500, width: 200, height: 50, label: "Timestamp" },
{ x: 800, y: 0, width: 400, height: 300, label: "Ad banner" },
],
});Use TurboSnap for speed
Enable TurboSnap to skip unchanged components:
npx ui-inspect build --project proj_id --turbosnapThis analyzes git changes to determine which screenshots need re-testing, often skipping 60-80% of captures.
Test multiple viewports
Always test at least desktop and mobile viewports. Responsive bugs are the most common visual regressions.
Run against a consistent environment
- Use a dedicated test database with seeded data
- Disable animations and transitions
- Use a consistent browser version (Puppeteer bundles Chromium)
- Set a consistent timezone and locale
Avoid testing against production data — it changes frequently and causes false positives.
Baseline management
Branch-based baselines
Baselines are scoped to branches. When a feature branch is merged:
# Promote approved baselines from feature branch to main
npx ui-inspect approve --project proj_id --allBatch operations
Approve or reject multiple baselines at once from the dashboard or API:
await client.baseline.batchApprove.mutate({
ids: ["baseline-1", "baseline-2", "baseline-3"],
});