A snapshot is a captured imprint of a value. On the first run, toMatchSnapshot serializes what you passed it and saves the result to a .snap file next to the test. On later runs Jest serializes the value again and compares it with the saved one: a match makes the test green, a divergence makes it red and shows the difference. The tool does not check that the value is correct - it checks that it has not changed since last time. It records a fact, it does not judge correctness.
Hence the temptation of naive use: snapshot everything. Rendered a component - snapshot the whole DOM. Got a response from an endpoint - snapshot the whole body. It feels like free coverage: one line, and a whole page is under control. The line is indeed one, but the control is an illusion.
It breaks in review. A snapshot of the whole DOM is hundreds of lines of markup that nobody reads. When such a snapshot turns red, the developer sees a giant diff mixing a meaningful change with a dozen incidental ones, shrugs, and runs jest -u without looking. The snapshot turns into a rubber stamp: it no longer records a contract, it records the last thing that happened to be on screen. This is the DOM dump - volume without meaning.
The reframe is simple: a snapshot is a reviewable contract. It should be small, serializable, and clear to a person seeing it for the first time. Snapshot not the whole world but a narrow value: a public DTO, the result of a pure function, an error message. For very short values there is toMatchInlineSnapshot - it keeps the expected value right in the test body rather than in a separate .snap. Then the review diff shows up in the same file as the code, and fooling yourself becomes impossible.
A distinct trap is generated fields. If an object carries an id or a creation date, the snapshot will turn red on every run, because those values change on their own rather than because of your code. The answer is property matchers: an asymmetric matcher (expect.any(String), expect.any(Date)) for a specific field. Jest checks such a field with the matcher and writes the matcher itself into the snapshot instead of the volatile value. The snapshot stays stable and turns red only on a real change of structure.
And the most important part - the discipline of the -u flag. The jest -u command rewrites snapshots with current values. It is not auto-fix and not a way to turn red into green. It is an act of accepting new behavior: you looked at the diff, confirmed the change is intended, and deliberately recorded the new norm. Using -u without looking at the diff throws away the one thing a snapshot exists for.
Cost and benefit balance out on size. A small, readable snapshot is cheap to maintain and catches unintended changes in the shape of data - it is justified for serializers, stable outputs, error messages. A large snapshot is expensive and useless: nobody reviews it, and it degrades into a stamp. The classic failure looks like this - a huge snapshot plus a reflex to run jest -u on every failure. The test is always green after the update, nobody reads the red diff, and what the snapshot supposedly guards is in fact guarded by nothing.
expect(container).toMatchSnapshot()expect(toPublicDto(user))
.toMatchInlineSnapshot(`
{
"role": "editor",
}
`)expect(user).toMatchSnapshot({
id: expect.any(String),
createdAt: expect.any(Date),
})