ScreenshotNeo

BlogGuides

Vitest Visual Testing: A Practical Guide

Set up Vitest screenshot comparisons in Browser Mode, manage baselines, reduce flaky captures, and diagnose visual diffs in CI.

By the ScreenshotNeo team4 October 20266 min read

Vitest visual testing checks whether a rendered page or element looks different from an approved screenshot. In Vitest 4, the built-in toMatchScreenshot() matcher provides this workflow in Browser Mode: capture a stable rendering, compare it with a reference image, then review any difference. It checks appearance; it does not prove that a button works or explain why a visual change occurred.

Browser Mode requires a provider. Vitest names Preview, Playwright, and WebdriverIO; its guide recommends Playwright for teams without an existing provider and says CI requires Playwright or WebdriverIO. Start with vitest init browser or configure a provider manually, following the documentation for your installed Vitest version. Vitest Browser Mode documentation

1. Configure Browser Mode

Initialize the browser project using the official command:

vitest init browser

Choose a provider supported by your intended environment. Preview can help you try Browser Mode; for CI, configure Playwright or WebdriverIO. The exact configuration depends on your Vitest version and project setup, so use the current Browser Mode guide rather than copying provider settings from another version. Keep visual tests in a distinct project or suite if that makes visual changes easier to review separately from ordinary behavior tests.

2. Write a screenshot assertion

Render the application state you intend to check in the test browser, then match the page or a selected element. For example:

import { expect, test } from 'vitest'
import { page } from 'vitest/browser'

test('primary button appearance', async () => {
  // Navigate or render the application so the target page is ready.
  await page.goto('/example')

  await expect(page.getByRole('button', { name: 'Continue' }))
    .toMatchScreenshot('primary-button')
})

The example assumes your application is available at /example in the configured browser test context. Adapt navigation and rendering to your app. The matcher accepts a name and options; consult the visual regression guide and the API for your installed release before setting matcher options or thresholds.

3. Review and maintain baselines

  1. Run the test once. The first visual run creates a reference image and fails with a message asking you to review it.
  2. Inspect the reference. Confirm it represents the intended design at the intended state, viewport, and content. Baseline approval is a human review step.
  3. Commit accepted references with the test suite so local runs and CI compare against the same files.
  4. Run again after code changes. Vitest captures the current rendering and compares it to the stored reference.
  5. Investigate failures. Review the reference, actual capture, and diff when available. Determine whether the change is intentional and whether it is acceptable.
  6. Update only for intended visual changes. The guide shows updating a visual regression project with vitest --project vrt --update. Review the newly generated reference before committing it.

A diff is evidence to investigate, not an automatic quality judgment. The documented diff can mark changed pixels red and anti-alias differences yellow when anti-aliasing is not ignored. A diff image is available when screenshot dimensions match; details can vary with matcher configuration.

4. Make screenshot output stable

Screenshot comparisons are sensitive to rendering conditions. Browser and browser version, operating system, fonts, graphics hardware, headless mode, display settings, viewport, and page state can all affect pixels. Use the same controlled environment for baseline creation and comparison, especially in CI.

Vitest retries captures to determine stability: it captures repeatedly until two consecutive screenshots match or the timeout is reached, then compares the stable capture with the reference. This helps with transient loading and rendering changes, but it cannot make endlessly changing content deterministic.

  • Wait for the content that matters, such as a target selector or loaded image, before asserting.
  • Disable or finish animations and transitions that keep changing pixels.
  • Use fixed test data and avoid timestamps, randomized values, rotating banners, and live content in the captured region.
  • Keep viewport, device scale, fonts, browser version, and headless settings consistent.
  • Prefer element-level captures when surrounding page content is irrelevant; use whole-page captures when layout across the page is what matters.

Thresholds trade sensitivity for tolerance: loosening a comparison can reduce failures from small rendering variation, while also making some real changes less likely to fail. A threshold does not remove all false positives. Choose settings based on the change you need to detect and keep the capture environment controlled.

5. Choose what the screenshot should cover

A whole-page comparison can reveal broad layout shifts and missing sections, but it also includes more content that may vary. An element-level comparison focuses a check on a component such as a button, card, or navigation bar. It is usually easier to diagnose, though it will not catch changes outside that element. Write tests around meaningful, stable states rather than taking screenshots indiscriminately.

Visual assertions complement behavior assertions. A screenshot can show that a control appears incorrectly, but cannot establish that it responds correctly. Keep interaction and accessibility checks in the suite as appropriate. Vitest’s guide explicitly cautions that toMatchScreenshot is not a substitute for proper assertions.

6. Troubleshoot common failures

Symptom Likely cause What to do
First run fails and creates an image No reference exists yet. Inspect the generated baseline, approve it only if it is the intended appearance, and commit it.
Many pixels differ on a developer machine but not CI Different browser, OS, fonts, graphics, display, or headless settings. Standardize the rendering environment and create or update references in that same environment.
Capture never stabilizes or times out Animation, continuously changing content, or a page that never settles. Disable animation, freeze dynamic data, wait for the required state, or capture a stable element.
Screenshot is blank or missing content The assertion ran before navigation, rendering, or image loading completed. Ensure the page is in the browser context and wait for the relevant selector or content before matching.
Diff image is not produced Reference and actual dimensions differ, or configured matcher behavior does not provide that artifact. Compare the actual and reference images directly, verify viewport and capture dimensions, and consult the matcher options for your version.
Update mode makes a failure disappear The reference was replaced without reviewing whether the change was correct. Review the actual capture and diff before accepting and committing updated references.

7. Performance, reliability, and cost

Each visual assertion needs browser rendering and one or more captures; stability retries can add work when a page takes time to settle. Keep the number of screenshots focused on meaningful states, scope assertions to relevant elements where possible, and avoid unnecessary repeated setup. A controlled CI browser environment improves repeatability and makes failures easier to compare over time.

Vitest’s built-in workflow is based on references stored with the test suite. The dossier does not establish a hosted service price or a universal speed benchmark for it. Account for browser setup, CI execution, baseline review, and artifact storage in your team’s workflow rather than assuming the matcher removes those costs.

Or skip the browser setup

If you need a screenshot of a deployed page without configuring a browser test provider, ScreenshotNeo is a website screenshot API and MCP server. A single request can return an image or PDF. It removes cookie banners, popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. AI agents can take screenshots through its MCP server. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000. See the API documentation.

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

Replace YOUR_API_KEY with your key. For automated UI regression tests, keep the rendering in the test browser and review committed references; use an API capture when a service-generated screenshot of a reachable URL fits the task.

Sign up free for 1,000 screenshots a month, with no card.

Frequently asked questions

How do I do visual regression testing with Vitest?

Configure Browser Mode with a provider, render a deterministic state, use toMatchScreenshot(), review and commit the initial baseline, then inspect differences on later runs.

How do I use toMatchScreenshot?

Call it on the page or element locator you want to capture after that target has rendered. Pass a stable name, and use documented options for your installed Vitest version.

Why is my Vitest screenshot test flaky?

Common causes include rendering environment differences and content that changes between captures. Standardize browser and display conditions, wait for the intended state, and disable or freeze changing content.

Does a matching screenshot prove that the UI works?

No. It checks appearance against a reference. Add behavior assertions for interaction and outcomes.

Sources