Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Vitest’s built-in visual regression workflow runs in Browser Mode: render a page or component in a browser, compare it with a committed reference image using toMatchScreenshot(), and review any differences before updating the baseline. For reliable results, keep visual tests in a separate Vitest project, pin the browser and runtime environment, and treat every baseline change as a code review decision—not an automatic fix.
What you need before you start
Visual regression testing checks whether a rendered interface has changed compared with an approved image. It complements unit and interaction tests: a screenshot can reveal an unexpected layout or styling change, but it cannot establish that a button works or that an application state is correct.
Vitest’s built-in screenshot comparison runs in Browser Mode. The documented workflow uses toMatchScreenshot() to compare a browser capture with a reference image. The feature was introduced in Vitest 4; check the documentation for your installed Vitest version before copying configuration, because Browser Mode APIs, providers, and defaults can change.
- A project using Vitest and a page or component rendering helper.
- Browser Mode configured with a provider. Vitest documents Preview, Playwright, and WebdriverIO providers; for headless CI, use Playwright or WebdriverIO rather than Preview.
- A repeatable browser environment for generating references and comparing them.
- A review process for approving new or changed screenshots.
Install and configure Browser Mode
Initialize Browser Mode
Start with Vitest’s interactive initializer:
npx vitest init browser
Follow the prompts for your project and provider. If you are setting up a Playwright-backed browser project directly, install the provider package:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutenpm install -D @vitest/browser-playwright
Then configure the Vitest Browser Mode project to use the playwright provider, following the configuration for your installed Vitest version. The exact project configuration is version-sensitive, so use Vitest’s Browser Mode and Playwright configuration documentation rather than copying a configuration from a different release.
Keep visual tests separate
Give visual regression tests their own project and filename pattern. For example, use **/*.vrt.test.[tj]s?(x) for visual tests and exclude that pattern from the unit-test project. This separation lets you run and interpret visual checks independently: a pixel difference does not get mixed into ordinary behavioral test failures.
Vitest’s documented command pattern is to invoke projects separately:
vitest --project unit
vitest --project vrt
Use the project names you actually configure. The important point is to make it possible to run unit tests and visual tests independently, both locally and in CI.
Control the browser conditions
A reference image is meaningful only when the capture conditions are repeatable. Keep the browser and its dependencies pinned, use the same operating system and CI image to create and compare references, and run headlessly in CI with a supported headless provider.
- Viewport: set a fixed viewport for each test. Vitest’s guide uses 1280 by 720 as an example, not a universal requirement. Choose dimensions that match the interface boundary you need to verify.
- Browser and operating system: differences in browser versions and operating systems can alter rendering. Do not generate a baseline on one environment and expect exact pixels from an uncontrolled environment on another.
- Fonts and display conditions: font availability, GPU, screen scaling, and headed versus headless execution can affect pixels. Keep these consistent where possible.
- Animation and changing content: moving animations and changing data make captures unstable. Disable or control them rather than increasing tolerance to hide them.
The viewport size is only one part of the rendering contract. Record the environment used for the references and keep the CI comparison environment aligned with it.
Write a visual test that checks the right boundary
Render the component through your application’s normal test helper, make behavioral assertions where they matter, and capture only the region whose appearance you intend to protect. This example follows Vitest’s documented assertion pattern; button must be obtained from the page after your project has rendered the relevant UI.
import { expect, test } from 'vitest'
import { page } from 'vitest/browser'
// Render the component using the application's normal test helper.
test('primary button looks correct', async () => {
const button = page.getByRole('button', { name: 'Save' })
await expect(button).toMatchScreenshot('primary-save-button')
})
Choose a stable, descriptive screenshot name. A component-level capture is usually a better regression boundary when you want to detect changes to that component: a whole-page image can fail because an unrelated region changed. Use a page capture when the page layout itself is what you need to protect.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesKeep functional expectations separate from the screenshot assertion. For example, assert the relevant state or interaction with ordinary test expectations, then use the image comparison to check appearance. A passing screenshot does not prove that the control responds correctly.
Create, inspect, and commit the first baseline
- Run the visual project for the first time. With no reference available, Vitest creates an image and reports that there was no prior reference.
- Open and inspect the generated image at the intended viewport. Check that the right content rendered, fonts and assets loaded, and the capture is not blank or in an unintended state.
- Run the test again to compare the capture with the reference.
- Commit the reviewed reference images alongside the test and code. Vitest’s guide describes references stored in
__screenshots__folders next to tests.
Do not approve a baseline solely because the first run generated it. An incorrect initial state becomes the image that future runs preserve, so review it as carefully as an expected-value assertion.
Read failures before changing a baseline
When a comparison fails, inspect the expected reference, the actual capture, and the generated diff image when one is available. In Vitest’s described diff output, red pixels indicate differences; yellow indicates anti-aliasing differences when anti-aliasing is not ignored. A diff image may not be generated if the compared images have different dimensions.
- First compare dimensions. A changed viewport or content size can cause a mismatch even when the underlying component looks right.
- Check the actual capture. Look for missing fonts or images, a different application state, late-loading content, or an overlay that obscures the intended UI.
- Inspect the location and shape of the diff. A localized change may be an intentional design edit; a broad shift can point to an environment or layout change.
- Only then decide whether the reference should change. A generated diff is diagnostic evidence, not approval.
Update references for intentional UI changes
When a design change is intentional, run the visual project with Vitest’s update option:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →vitest --project vrt --update
Inspect the changed images, then commit only the approved references with the corresponding code. Updating every failing image without reviewing it can turn an accidental regression into the new expected appearance.
Vitest does not automatically remove screenshots associated with deleted or renamed tests. Remove stale references as part of test cleanup so they do not linger as confusing, unowned artifacts.
Make captures stable when pages are dynamic
Animations and transitions
Vitest’s built-in assertion disables animations by default with the Playwright provider. For additional control, a setup stylesheet can suppress animations and transitions. Pages with endless or continuously moving content may never produce two consecutive identical captures: Vitest repeatedly captures until two consecutive images match or the timeout is reached. Make motion deterministic or disable it for the visual test instead of treating a timeout as a reason to accept a different image.
Rank #4
Timestamps, user data, and other variable regions
Mock the data source when the test should always show the same timestamp, account details, or other changing content. With the Playwright provider, screenshot options can also mask a changing region. Mask only the part whose exact pixels are not the subject of the test; overly broad masks can conceal genuine layout regressions.
Fonts and late-loading assets
If a capture intermittently differs, check whether the same fonts and assets are available and loaded in both environments. A different font can change line wrapping and move neighboring elements. Make the test wait for the relevant page state or selector before capture rather than relying on an arbitrary pause when a meaningful readiness condition exists.
Choose a comparison tolerance deliberately
Pixel comparison is sensitive to small rendering differences. Vitest’s guide shows how to configure a comparator and options including a per-pixel threshold and allowedMismatchedPixelRatio. A ratio scales the allowed mismatch with screenshot size, but no sample value is a universal default: tolerance depends on the application, the controlled environment, and the visual variation the team is willing to accept.
Start by stabilizing the browser and content. Then review real diffs and choose a tolerance that addresses acceptable rendering variation without hiding changes the test is meant to catch. Document why the tolerance exists, and revisit it if the application or browser environment changes. A more permissive threshold is not a substitute for investigating inconsistent captures.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Run visual checks locally and in CI
Make the two suites explicit in package scripts so contributors can run the relevant check without guessing. For example:
Recommended Free Tools
Best Value
{
"scripts": {
"test:unit": "vitest --project unit",
"test:vrt": "vitest --project vrt"
}
}
In CI, install the selected browser and run the visual project in the same pinned environment used to create or update references. A CI job that silently uses a different browser or operating system can produce noisy failures that are unrelated to the code change.
Visual runs also need a deliberate update path: ordinary CI should compare against committed references, while an intentional redesign should be reviewed and committed as a baseline update. Keep screenshots in version control with the test changes so reviewers can see what appearance changed.
Troubleshooting common failures
- Browser Mode cannot launch headlessly: confirm that the project uses Playwright or WebdriverIO for headless execution; Vitest’s Preview provider is not the documented choice for headless runs.
- Every run produces a different image: align browser, operating system, viewport, fonts, and headless mode. Then mock changing data and suppress animations that should not be part of the comparison.
- The test times out waiting for a stable image: look for endless animation or content that keeps changing. Make the page deterministic or disable the motion for the test.
- No diff image appears: check whether the expected and actual dimensions differ; Vitest may not generate a diff image in that case.
- The whole page fails after an unrelated edit: narrow the capture to the component or region that defines the regression boundary, if that is what the test is intended to protect.
- Old screenshot files remain after renaming a test: remove the references for deleted or renamed tests during cleanup; they are not automatically deleted.
- A tolerance change makes failures disappear: inspect the raw expected and actual images first. Increase tolerance only for understood and acceptable pixel variation.
Or skip the browser setup
ScreenshotNeo is a screenshot API, not a replacement for Vitest’s browser assertion, committed baselines, or comparison workflow. It can be useful when you need a hosted screenshot capture—for example, to capture a URL outside your test runner. One GET request returns an image or PDF; the example below saves a WebP capture of Stripe. See the ScreenshotNeo documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Before capture, ScreenshotNeo can accept cookie or consent banners and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides screenshot tools for AI agents, including Claude and Cursor. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to try it with 1,000 screenshots a month and no card. Learn more at ScreenshotNeo.
Practical checklist
- Browser Mode uses a provider appropriate to the run environment.
- Visual tests have a separate project and are excluded from the unit project.
- The screenshot boundary, viewport, browser, and comparison environment are intentional and repeatable.
- The first baseline and every update have been inspected before being committed.
- Changing data and animation are controlled, and any tolerance is justified by reviewed differences.
- Behavioral assertions remain in place for interaction and state.
Frequently Asked Questions
Can Vitest visual regression tests run without a browser?
No. Vitest’s built-in screenshot comparison runs in Browser Mode, so the test must render in a configured browser environment.
Does `toMatchScreenshot()` replace interaction tests?
No. It compares appearance. Keep separate assertions for behavior, state, and interactions that matter to the test.
Should I use a whole-page screenshot for every test?
No. Capture the component or region that represents the intended regression boundary; use a page capture when the page-level layout is what you need to verify.
Free tools Windows power users keep installed
One-click scans. No signup required.
Quick Recap
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.




