Visual Regression Testing with Vitest
High-level pointers for setting up visual regression testing with Vitest v4's browser mode. This is not a full tutorial โ the official Vitest guide is the source of truth; this page covers the key decisions and caveats. See the Vitest tech-stack page for what Vitest is and why we use it, and the Testing Strategy for where visual regression fits in the test pyramid.
What visual regression tests coverโ
Visual regression tests catch unintended UI changes by comparing screenshots of components or pages against a stored baseline. A pixel diff that exceeds a threshold fails the test, surfacing broken layouts, colour shifts, or missing elements that functional assertions would miss.
How it works in Vitest v4โ
Vitest v4 added built-in visual regression testing to its browser mode, which runs tests inside real browsers instead of a simulated DOM, making screenshot-based assertions practical โ keeping them consistent across environments is the main caveat (see below).
- Install
vitestand@vitest/browser-playwright(plusvitest-browser-reactfor component rendering), then importplaywrightfrom@vitest/browser-playwrightand setprovider: playwright()in your config. The defaultpreviewprovider uses simulated events and is discouraged for real visual testing โ Playwright (or WebdriverIO) is required for screenshots and CI. - Use the built-in
toMatchScreenshot()matcher on a locator to capture and compare renders against a stored baseline. - Tests run against real browser engines โ Chromium, Firefox, and WebKit โ via the Playwright provider, without needing a separate E2E harness.
import { render } from 'vitest-browser-react';
import { expect, test } from 'vitest';
import { Badge } from './Badge';
test('Badge renders correctly', async () => {
const screen = render(<Badge label="New" variant="success" />);
await expect(screen.getByText('New')).toMatchScreenshot('badge-success');
});
toMatchScreenshot(name?, options?) accepts an optional name plus comparator options (comparatorOptions.threshold, comparatorOptions.allowedMismatchedPixelRatio) and screenshotOptions (e.g. Playwright mask for dynamic content) โ see the official guide for the full API.
- Baselines live in
__screenshots__folders next to the test files, named<test-name>-<browser>-<platform>.pngโ commit these. - Actual/diff artifacts from failures go to
.vitest/attachments/โ these are transient, so gitignore them. - The first run fails by design: a new reference screenshot is created and must be reviewed before re-running.
- Update baselines intentionally with
vitest --update(-u) when a UI change is deliberate. - Stale screenshots for deleted or renamed tests are not cleaned up automatically.
Keeping screenshots stableโ
- CI/OS consistency is the big caveat. Screenshots differ across operating systems and browser builds (fonts, antialiasing), so baselines generated on one platform won't match another.
- Flakiness mitigations:
- The Playwright provider disables animations by default (
animations: 'disabled'). - Vitest retakes screenshots until the page stabilizes (built-in stability detection).
- Wait for
document.fonts.readybefore asserting. - Mask or mock dynamic content (timestamps, user data) via
screenshotOptions.mask. - Set an explicit viewport.
- The Playwright provider disables animations by default (
Never run --update locally if CI runs on a different OS. The official guide recommends running visual tests in Docker, using a CI-only workflow (e.g. a manually-triggered GitHub Actions job that regenerates baselines on Ubuntu), or a cloud service.
When to use Playwright insteadโ
Playwright also supports screenshot assertions and is the better choice for full-page or flow-level visual checks within an E2E test. Vitest browser mode visual regression is best suited for isolated component snapshots during development. See the Playwright E2E Recipe for setting up flow-level tests.
Resourcesโ
- Vitest โ what it is, why we use it
- Testing Strategy โ how visual regression fits in the bigger picture
- Vitest Visual Regression Testing guide โ official setup, comparator options, and CI workflow recipes
- Vitest Browser Mode docs
- Playwright E2E Recipe โ flow-level testing with screenshot assertion support