Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Vitest visual regression testing runs in Browser Mode and compares a fresh browser capture with a committed reference image using toMatchScreenshot(). A reliable setup keeps visual tests in their own project, pins the browser and rendering environment, controls dynamic content, and requires a human review of every baseline change.

What Vitest visual regression testing does

Visual regression testing detects unintended changes to rendered pixels. Vitest launches a real browser through Browser Mode, captures the page or element you select, and compares that image with a reference stored beside the test. The first approved run creates the reference; later runs fail when the capture differs beyond your configured tolerance.

This complements, rather than replaces, behavioral tests. A screenshot can show that a Save button changed color or moved, but it cannot prove that clicking the button saves data. Keep interaction and state assertions in the same test or in your unit/component suite.

Prerequisites and provider choice

Install Browser Mode

Use Vitest’s interactive initializer:

npx vitest init browser

For a Playwright-backed setup, install the provider package:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npm install -D @vitest/browser-playwright playwright

Vitest also documents WebdriverIO and preview providers. Headless execution requires Playwright or WebdriverIO; the preview provider is not a headless browser.

Choose a repeatable browser environment

  • Pin Vitest, the browser provider, Playwright (or WebdriverIO), and the browser version.
  • Generate and compare references on the same operating system and CI image.
  • Keep GPU settings, installed fonts, screen scaling, headed/headless mode, and viewport dimensions consistent.
  • Use a fixed viewport. Vitest’s example uses 1280 by 720; treat that as an example, not a universal requirement.

Separate visual tests from unit tests

Create a visual project with a naming convention such as **/*.vrt.test.[tj]s?(x), and exclude those files from the unit project. Separate projects let a pixel mismatch remain visible instead of being buried among behavioral failures.

import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'

export default defineConfig({
  test: {
    projects: [
      {
        name: 'unit',
        include: ['src/**/*.{test,spec}.{js,ts,jsx,tsx}'],
        exclude: ['**/*.vrt.test.{js,ts,jsx,tsx}'],
      },
      {
        name: 'vrt',
        include: ['src/**/*.vrt.test.{js,ts,jsx,tsx}'],
        browser: {
          enabled: true,
          provider: playwright(),
          instances: [{ browser: 'chromium' }],
          headless: true,
          viewport: { width: 1280, height: 720 },
        },
      },
    ],
  },
})

Adjust the include patterns to your repository. The important properties are a distinct visual project, a browser provider, headless execution, and a fixed viewport.

Write a screenshot test in TypeScript

Render the component with your normal application test helper, locate the intended regression boundary, and call toMatchScreenshot().

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import { expect, test } from 'vitest'
import { page } from 'vitest/browser'

// Mount your component with the same helper used by other browser tests.
test('primary button looks correct', async () => {
  const button = page.getByRole('button', { name: 'Save' })

  // Keep functional checks separate from the visual assertion.
  await expect(button).toBeVisible()
  await expect(button).toMatchScreenshot('primary-save-button')
})

Use an element capture when the component is the boundary you own. A whole-page capture is useful for layout and routing regressions, but it also includes unrelated content, making failures noisier.

Create, review, and commit reference images

  1. Run the visual project for the first time. With no reference present, Vitest reports that a baseline must be created.
  2. Open the generated image and verify fonts, content, spacing, focus state, and responsive dimensions.
  3. Run the same project again. It now compares the new capture with the reference.
  4. Commit the approved reference files. Vitest stores them in __screenshots__ folders next to the tests.

References are source-controlled test artifacts. A baseline accepted without inspection can permanently encode a broken layout.

Run visual tests locally and in CI

Define separate scripts so developers can target either suite:

{
  "scripts": {
    "test:unit": "vitest --project unit",
    "test:vrt": "vitest --project vrt"
  }
}

CI should install the pinned browser and execute vitest --project vrt on the same image used to generate references. Do not regenerate baselines on a different operating system and expect pixel-identical output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Control sources of unstable screenshots

Animations and transitions

Vitest’s built-in assertion disables animations by default with the Playwright provider. You can also load a setup stylesheet that sets animation and transition durations to zero. An endlessly moving page may never produce two consecutive matching captures and can time out.

Dynamic data

Mock timestamps, randomized identifiers, network responses, and user-specific content. With the Playwright provider, mask a changing region through the screenshot options when masking is preferable to mocking.

Fonts and rendering differences

Missing fonts, a changed browser build, GPU differences, operating-system text rendering, or a different device scale can produce diffs even when CSS did not change. Install the same fonts and use the same CI image for baseline creation and comparison.

Waiting for a stable page

Stable screenshot detection repeatedly captures until two consecutive captures match or the timeout is reached. Wait for the application’s loaded state or a meaningful selector before taking the screenshot instead of relying on an arbitrary short delay.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Configure comparison tolerance deliberately

Exact pixel equality is often too strict for anti-aliasing. Vitest supports comparator configuration, a per-pixel threshold, and allowedMismatchedPixelRatio. A ratio scales tolerance with image size, but there is no universal correct value. Start conservatively, review real failures, and document why your chosen tolerance is acceptable for the product.

Do not raise thresholds to make a failing build green without examining the image. A tolerance can hide a one-pixel border change, a shifted element, or a large low-contrast regression.

Diagnose a mismatch

  1. Open the expected reference, the actual capture, and the generated diff image.
  2. Determine whether the change is intentional (for example, an approved redesign) or environmental (font, browser, viewport, data, or animation).
  3. For anti-aliasing differences, check the comparator settings and rendering environment before changing application code.
  4. If dimensions differ, note that a diff image may not be generated; compare the two images directly and verify viewport and responsive breakpoints.
  5. Fix the cause, rerun the project, and review the result again.

Diff colors are diagnostic: Vitest’s guide describes red pixels as differences and yellow pixels as anti-aliasing differences when anti-aliasing is not ignored.

Update baselines safely

When a UI change is intentional, run the visual project with --update, inspect every changed image, and commit the approved references together with the code change.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
vitest --project vrt --update

Never treat the ability to update as approval. Review the resulting files in code review. Screenshots for deleted or renamed tests are not automatically removed, so delete stale references during test cleanup.

Common failures and fixes

“No browser provider” or browser launch errors

Cause: Browser Mode is enabled without a supported provider or the browser is not installed. Fix: install @vitest/browser-playwright and Playwright, run the provider’s browser installation step in your project, and verify the visual project configuration.

Every run reports differences

Cause: mismatched OS, browser, fonts, viewport, GPU, scale factor, or headed/headless mode. Fix: pin versions and run both baseline generation and CI comparisons on one image.

The test times out waiting for stability

Cause: an animation, carousel, clock, or continuously changing network response. Fix: disable motion, mock the data, wait for a stable selector, or mask the dynamic region.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Only the first run fails because no reference exists

That is expected. Inspect the generated image, then commit it as the approved baseline and rerun.

A redesign created hundreds of failures

Confirm the redesign is intentional, update only the affected references with --update, and inspect the complete diff. Do not change a global tolerance to bypass review.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Performance, reliability, and maintenance

  • Capture the smallest meaningful element to reduce rendering work and unrelated failures.
  • Run unit and visual projects independently so a slow browser suite does not obscure fast feedback.
  • Keep test data deterministic and avoid live third-party content.
  • Use one pinned CI image for all visual jobs; parallelize only when each worker has the same browser and font environment.
  • Review reference files when tests are renamed, removed, or moved, because old images can remain behind.

Or skip the browser setup

If you need an image of a deployed page rather than a repository-controlled Vitest baseline, ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled.

Only clean shots are billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are free, and response headers report the page verdict and whether the request was billed. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo documentation for the full option set, including full-page and selector captures, device presets, retina scale, dark mode, custom CSS and JavaScript, clicks, waits, blocked requests, headers, cookies, user agents, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed links, asynchronous webhooks, bulk capture, usage data, and the OpenAPI specification.

The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account.

Frequently asked questions

Does a screenshot test replace accessibility testing?

No. It can reveal visible regressions, but it does not replace semantic, keyboard, or assistive-technology checks.

Should baselines be stored in Git?

Yes, commit reviewed references next to the tests so changes are versioned with the code that produced them.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When should I capture a whole page?

Use whole-page captures for page-level layout contracts. Prefer an element capture when the intended contract is a specific component.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.