UI Inspect
Guides

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:

ui-inspect.config.yml
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-approve

The --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_NUMBER

Review and approve

When diffs are found:

  1. Check the PR comment or dashboard for visual diffs
  2. If intentional, approve the new baselines: npx ui-inspect approve
  3. 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.2

Handle 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 --turbosnap

This 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 --all

Batch 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"],
});

On this page