Snapshot testing overview
Snapshot tests capture the output of a piece of code and save it to a file. On subsequent runs, the output is compared against the saved snapshot. If the output changes, the test fails. This approach is useful when testing code that produces structured output such as functions returning complex objects, components rendering HTML, or error formatters producing multi-line messages.
toMatchSnapshot matcher
The toMatchSnapshot() matcher is used to create a snapshot test. Pass a value to toMatchSnapshot() to capture it. On the first run, Vitest creates a snapshot file. On subsequent runs, the output is compared against the stored snapshot.
Snapshot file location and naming
When you create your first snapshot test, Vitest stores the snapshot in a __snapshots__ directory next to your test file. The snapshot file is named with the test filename plus .snap extension, for example __snapshots__/example.test.js.snap. Each snapshot within the file is labeled with an export key like 'generates a greeting 1'.
Inline snapshots with toMatchInlineSnapshot
The toMatchInlineSnapshot() matcher keeps the snapshot right in your test file instead of in a separate .snap file. You start by writing the assertion without any argument, and when you run the test, Vitest automatically fills in the snapshot as a string argument to toMatchInlineSnapshot(). Unlike external snapshots, inline snapshots don't create separate .snap files; the expected value is stored directly in your test file.
Updating snapshots in watch mode
In watch mode, you can press 'u' in the terminal to update all failed snapshots.
Updating snapshots from CLI
You can update snapshots from the command line by running 'vitest -u' or 'vitest --update' to update snapshots and exit.
Updating snapshots in VS Code
In VS Code, you can use the 'Update Snapshots' command on the test gutter icon from the Vitest extension to update snapshots.
File snapshots with toMatchFileSnapshot
The toMatchFileSnapshot() matcher lets you save the snapshot to a file with any extension you want. The snapshot is stored as a plain file (e.g., .html) that you can open in a browser, view with syntax highlighting, or diff with standard tools. This works well for HTML, SVG, CSS, generated code, or any output where the file format matters for readability.
When to use snapshots
Snapshots are useful for testing structured, serializable output that would be tedious to assert on manually. Common use cases include functions returning complex configuration objects with many nested fields, HTML or markup generated by rendering functions or template engines, error messages with formatted stack traces or context information, CLI output or log messages with specific formatting, and JSON API responses where you want to catch any unexpected field changes.
When not to use snapshots
Snapshots are not the best tool when the output changes frequently (for instance, it includes timestamps or random IDs), because you will spend more time updating snapshots than they save you. If you only care about one or two specific fields, a targeted assertion like toMatchObject() or toHaveProperty() expresses your intent more clearly than a snapshot that captures everything.
General rule for snapshot vs targeted assertions
Use snapshots when you want to protect against any change in the output. Use targeted assertions when you only care about specific properties.
Handling dynamic values in snapshots with property matchers
If your output includes values that change every run like timestamps or IDs, you can use property matchers to pin the structure while ignoring volatile fields. Pass an object with asymmetric matchers as the first argument to toMatchSnapshot() or toMatchInlineSnapshot(). For example, you can use expect.any(Number) to match any number and expect.any(Date) to match any date, while other fields are snapshotted as usual.
Error snapshots with toThrowErrorMatchingInlineSnapshot
The toThrowErrorMatchingInlineSnapshot() matcher combines toThrow() with toMatchInlineSnapshot() to capture error messages as inline snapshots without a separate .snap file. This is especially useful for verifying that error messages are clear and don't accidentally change. Like other inline snapshots, Vitest fills in the string on the first run and updates it when you press 'u'.
Commit snapshots to version control
Snapshot files should be committed to version control as they serve as a record of the expected output and should be reviewed in code review just like any other test assertion.
Snapshot update pitfall
When updating snapshots, always review the diff to confirm the changes are intentional and not a bug. It is easy to accidentally accept a broken output by blindly pressing 'u'.
First snapshot test example
Example of creating a snapshot test: import { expect, test } from 'vitest'; function generateGreeting(name) { return { message: `Hello, ${name}!`, timestamp: null, version: 2, } }; test('generates a greeting', () => { expect(generateGreeting('Alice')).toMatchSnapshot() }). The first time this test runs, Vitest creates a snapshot file with the serialized output. On subsequent runs, it compares the output against this stored snapshot.
Inline snapshot example
Example of using toMatchInlineSnapshot(): test('generates a greeting', () => { expect(generateGreeting('Alice')).toMatchInlineSnapshot(`{
"message": "Hello, Alice!",
"timestamp": null,
"version": 2,
}`) }). When you run the test initially without an argument, Vitest automatically fills in the snapshot as a string argument.
File snapshot example
Example of using toMatchFileSnapshot(): test('renders the component', async () => { const html = renderComponent(); await expect(html).toMatchFileSnapshot('./fixtures/component.html') }). The snapshot is stored as a plain .html file with proper syntax highlighting.
Dynamic values in snapshot example
Example of handling dynamic values with property matchers: test('user snapshot with dynamic fields', () => { const user = createUser('Alice'); expect(user).toMatchSnapshot({ id: expect.any(Number), createdAt: expect.any(Date), }) }). The id and createdAt fields are checked against the matchers instead of being compared to stored values, while all other fields are snapshotted as usual.
Error snapshot example
Example of using toThrowErrorMatchingInlineSnapshot(): test('throws on invalid input', () => { expect(() => parse('')).toThrowErrorMatchingInlineSnapshot(`[Error: Unexpected end of input at position 0]`) }). This captures the error message as an inline snapshot.
toMatchSnapshot() API for basic snapshots
Use expect().toMatchSnapshot() to capture and compare a value against a stored snapshot file. The first run creates a snapshot file with the serialized value. On subsequent runs, Vitest compares the current output with the stored snapshot. The test passes if they match, and fails if they don't match, indicating either a bug or that the snapshot needs updating.
Snapshot file format and location
Vitest creates a snapshot file with the format 'Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html' as the header comment. Snapshot entries are stored as exports like exports['test name 1'] = 'serialized value'. The snapshot artifact should be committed alongside code changes and reviewed as part of code review.
toMatchInlineSnapshot() for inline snapshots
Use expect().toMatchInlineSnapshot() to store snapshots directly in the test file instead of a separate file. When first run, Vitest modifies the test file to add the snapshot as a string argument: expect(result).toMatchInlineSnapshot('"expected value"'). This allows viewing expected output without switching files.
Update snapshots with -u flag or u key
Update snapshots with the CLI flag --update or -u: vitest -u. In watch mode, press the 'u' key in the terminal to update failed snapshots directly. Use this when snapshot changes are expected and the new output should be stored.
CI behavior for snapshot updates
By default, Vitest does not write snapshots when CI environment variable (process.env.CI) is truthy. Any snapshot mismatches, missing snapshots, or obsolete snapshots will fail the CI run. An obsolete snapshot is a snapshot entry that no longer matches any collected test, usually from removed or renamed tests.
toMatchFileSnapshot() for file snapshots
Use expect(result).toMatchFileSnapshot('./path/to/file.extension') to compare output against an explicit file instead of a .snap file. This avoids escaping special characters (double-quote and backtick) and allows custom file extensions for syntax highlighting. Example: await expect(result).toMatchFileSnapshot('./test/basic.output.html'). Can be updated with the --update flag.
toMatchScreenshot() for visual regression testing
Use expect(element).toMatchScreenshot('snapshot-name') in browser mode to capture and compare screenshots for visual regression testing. This detects unintended visual changes in UI components and pages. Requires browser mode enabled.
ARIA snapshots for accessibility testing
Use expect.element(page.getByRole('navigation')).toMatchAriaInlineSnapshot() to capture and assert the accessibility tree of a DOM element. ARIA snapshots provide a semantic alternative to visual regression testing, asserting structure and meaning rather than pixels. Based on Playwright's ARIA snapshots. Available from Vitest 4.1.4.
expect.addSnapshotSerializer() for custom serialization
Add custom snapshot serializers with expect.addSnapshotSerializer({serialize, test}). The serialize function receives (val, config, indentation, depth, refs, printer) and returns a serialized string. The printer function serializes values using existing plugins. The test function returns true if the serializer should handle the value. Example: expect.addSnapshotSerializer({serialize(val, config, indentation, depth, refs, printer) { return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`}, test(val) { return val && Object.prototype.hasOwnProperty.call(val, 'foo') }})
snapshotSerializers config option for implicit serializers
Add custom serializers implicitly via the snapshotSerializers config option in vitest.config.ts. Create a serializer file implementing SnapshotSerializer interface with serialize(val, config, indentation, depth, refs, printer) and test(val) methods, then reference it: test: { snapshotSerializers: ['path/to/custom-serializer.ts'] }. This avoids manually calling expect.addSnapshotSerializer in every test file.
Custom snapshot matchers with Snapshots composables
Build custom snapshot matchers using composable functions from Snapshots: const { toMatchFileSnapshot, toMatchInlineSnapshot, toMatchSnapshot } = Snapshots. Call these with expect.extend() to create matchers that transform values before snapshotting. Example: expect.extend({toMatchTrimmedSnapshot(received: string, length: number) { return toMatchSnapshot.call(this, received.slice(0, length)) }}). The composables return {pass, message} for customization.
Custom snapshot matchers parameter order rule
For inline snapshot matchers, the snapshot argument must be the last parameter or second-to-last when using property matchers. Vitest rewrites the last string argument in the source code, so custom arguments before the snapshot work, but custom arguments after it are not supported.
Async custom snapshot matchers must use chai error flag
When a custom inline snapshot matcher is asynchronous, Vitest cannot automatically infer the call location for inline snapshot rewriting. Capture the call site by setting the 'error' flag on the chai assertion object at the top of the matcher: chai.util.flag(this.assertion, 'error', new Error()). File snapshot matchers must be async and return a Promise.
TypeScript Matchers interface augmentation for custom snapshots
Augment the Matchers<R, T> interface to add TypeScript support for custom snapshot matchers. Example: declare module 'vitest' { interface Matchers<R, T> { toMatchTrimmedSnapshot: (length: number) => R; toMatchTrimmedInlineSnapshot: (inlineSnapshot?: string) => R; toMatchTrimmedFileSnapshot: (file: string) => Promise<void>; } }
DomainSnapshotAdapter interface for custom comparison
A domain snapshot adapter implements four methods: name (string identifier), capture(received): extracts structured data, render(captured): serializes to snapshot string, parseExpected(input): parses stored snapshot into expected value, match(captured, expected): compares and returns {pass, message?, resolved?, expected?}. The adapter is generic over Captured type (what value actually is) and Expected type (what stored snapshot parses into). This enables custom comparison logic beyond string equality.
DomainMatchResult resolved and expected fields
The match() method's DomainMatchResult can include: resolved (captured value viewed through template's lens, preserving patterns, used for diffs and --update), expected (stored template re-rendered as string, used for expected side of diffs). When omitted, resolved falls back to render(capture(received)) and expected falls back to raw snapshot string.
toMatchDomainSnapshot() and toMatchDomainInlineSnapshot() composables
Register custom domain snapshot matchers with expect.extend() calling Snapshots.toMatchDomainSnapshot.call(this, myAdapter, received) and Snapshots.toMatchDomainInlineSnapshot.call(this, myAdapter, received, inlineSnapshot). This integrates the domain adapter into the snapshot lifecycle (creation, update, inline rewriting).
Snapshot serialization with @vitest/pretty-format
Vitest stores a serialized representation of received values using @vitest/pretty-format. Snapshot rendering can be configured with the snapshotFormat config option for general formatting behavior. Vitest sets printBasicPrototype to false by default (unlike Jest) for cleaner output.
printBasicPrototype config default difference from Jest
Vitest sets printBasicPrototype to false by default, removing 'Array' and 'Object' wrapper text in snapshots for readability. Jest <29.0.0 defaults to true. To use Jest behavior, configure: test: { snapshotFormat: { printBasicPrototype: true } }
Snapshot separator differences from Jest
Vitest uses chevron '>' as separator instead of colon ':' for custom messages in snapshot names. Example: In Jest 'exports[`test: hint 1`]', in Vitest 'exports[`test > hint 1`]'. This applies to toThrowErrorMatchingSnapshot and similar APIs.
Error snapshot format differences from Jest
Vitest's toThrowErrorMatchingSnapshot and toThrowErrorMatchingInlineSnapshot snapshot Error instances differently than Jest. Jest snapshots Error.message for throw cases, Vitest prints the full error: [Error: error]. Both snapshot the full error in toMatchInlineSnapshot(expect(new Error('error'))).toMatchInlineSnapshot(`[Error: error]`).
Async concurrent tests require local Test Context expect
When using Snapshots with async concurrent tests, use expect from the local Test Context instead of the module-level import to ensure the right test is detected. This prevents snapshot association issues in concurrent test execution.
expandSnapshotDiff flag shows full snapshot diff
The `--expandSnapshotDiff` CLI flag shows full diff when snapshot fails.
Vue snapshot serializer in Vitest
If migrating Vue tests from Jest, install jest-serializer-vue package and specify it in snapshotSerializers config to avoid escaped quotes in snapshots.
Custom snapshot matchers with Snapshots from vitest
In Vitest, import Snapshots from vitest and destructure toMatchSnapshot and toMatchInlineSnapshot: import { Snapshots } from 'vitest'; const { toMatchSnapshot, toMatchInlineSnapshot } = Snapshots. Use in expect.extend() for custom snapshot matchers.