Visual and Snapshot Testing
Purpose and Scope
This page helps teams decide how to use visual tests and snapshot tests as complementary regression signals for Storybook components. A regression signal is the evidence a future change produces when it breaks an existing expectation. Visual tests answer whether a story still looks correct when compared with a known good baseline. Snapshot tests answer whether the rendered document or markup still matches a stored textual representation. Storybook’s testing guidance treats stories as the shared scenario format, so the same component state can support design review, documentation, interaction checks, accessibility checks, and targeted snapshot assertions. Sources: docs/writing-tests/snapshot-testing.mdx
The important distinction is reviewer experience. The snapshot testing documentation says snapshots are convenient to create, but warns that they can be difficult and noisy to maintain when they contain too much information. That is why the docs point most UI component work toward visual tests, which are easier to review, or interaction tests, which are focused on functionality. Snapshot tests are still valuable when the serialized output is the thing being protected, such as a small HTML contract, an exceptional rendering branch, or a case where an error must be represented consistently. Sources: docs/writing-tests/snapshot-testing.mdx
Relevant Source Files
- docs/writing-tests/snapshot-testing.mdx — Defines Storybook’s snapshot testing guidance, recommends Portable Stories instead of Storyshots, lists supported environments, and describes the compose, render, and compare workflow for story-based snapshots.
Choosing Visual Tests or Snapshot Tests
Use visual tests when the question is whether a component still appears correctly to a user. The official visual testing flow turns stories into visual checks by taking snapshots of rendered stories and comparing them with baselines. That makes it well suited to design systems, layout variants, responsive states, theme changes, and styling regressions that are hard to express as text assertions. Because every story already names a component state, the visual workflow scales across the same examples that designers, engineers, and product reviewers use during normal Storybook development.
Use snapshot tests when the rendered structure is intentionally part of the contract. The repository documentation defines snapshot testing as rendering a component in a given state, capturing the rendered DOM or HTML, and comparing it against the previous snapshot. That means the test is strongest when the output is small and meaningful. A broad snapshot of class-heavy markup can produce a failure that is technically accurate but hard to review. A focused snapshot around a small branch, empty state, generated message, or error presentation is more likely to provide a useful maintenance signal. Sources: docs/writing-tests/snapshot-testing.mdx
Portable Stories Workflow
Portable Stories are the bridge between story files and external test runners. The documentation explains that Storybook composes stories with their annotations, including args, decorators, parameters, and related metadata, then produces a renderable element for tests. This matters because real stories are often more than a component invocation. They may rely on theme providers, routing context, layout wrappers, global parameters, or default args. By reusing the composed story, a snapshot test exercises the same scenario a developer sees in Storybook instead of recreating a parallel test fixture by hand. Sources: docs/writing-tests/snapshot-testing.mdx
The documented targets for Portable Stories are Vitest, Jest, and Playwright Component Testing. If a project uses Storybook Test, the snapshot page says the project is already configured to use Portable Stories in Vitest. If the project does not use Storybook Test, or if it needs another environment, the next step is to follow the relevant Portable Stories documentation for that runner and apply project-level annotations. The task flow is then consistent: import the stories, compose them, render the selected story in the test environment, and assert against the generated snapshot. Sources: docs/writing-tests/snapshot-testing.mdx
Source-to-Workflow Mapping
The snapshot page describes three connected responsibilities. Story files own the component states, so they should remain the canonical examples of meaningful UI variations. Portable Stories owns the adaptation layer, preserving story annotations so tests do not drift away from Storybook behavior. The test runner owns execution and comparison, including creating the first stored snapshot and failing later runs when the received output differs. Keeping those responsibilities separate prevents snapshots from becoming a second story format and keeps review focused on the actual regression: changed appearance, changed behavior, or changed serialized output. Sources: docs/writing-tests/snapshot-testing.mdx
When a test fails, the repository documentation shows a diff-style failure where the expected and received rendered markup differ. That example is useful because it exposes both the value and the cost of snapshots. The test can catch an unintended markup change automatically, but the diff may include many framework or utility-class details. Treat that as a design constraint. If reviewers regularly cannot tell whether a snapshot failure matters, replace broad snapshots with visual coverage or narrower assertions, and keep only the snapshots that express a deliberate contract. Sources: docs/writing-tests/snapshot-testing.mdx
Example Test Shape
The example below shows the shape of a Portable Stories snapshot test. The story still defines the component state, while the test file renders the composed story and asks the runner to compare the result. Adapt the imports and renderer utilities to the framework and test environment used by the project.
import { render } from '@testing-library/react';
import { composeStories } from '@storybook/react';
import * as stories from './Button.stories';
const { Primary } = composeStories(stories);
test('Button snapshot', () => {
const { container } = render(<Primary />);
expect(container).toMatchSnapshot();
});This pattern should be applied selectively rather than generated indiscriminately for every story. A visual testing service can turn every story into a broad appearance check, which is appropriate because humans review image differences at story granularity. Text snapshots are different: they produce stored artifacts that developers must read, approve, and update. Start with the smallest set of snapshots that protect output-level contracts. Then use story naming, clear test names, and focused rendered regions so a future failure explains what behavior the team intended to preserve.
Migration and Maintenance Guidance
Storyshots should not be the starting point for new snapshot work. The snapshot documentation includes an explicit callout that Storyshots is deprecated and no longer maintained, and it recommends the Portable Stories API instead. For existing suites, migration is more than a mechanical library swap. It is a chance to delete low-value blanket snapshots, move broad UI regression coverage to visual testing, and rewrite the remaining cases as intentional runner-specific tests. That produces fewer approvals, clearer failures, and a test suite that stays closer to how Storybook renders stories today. Sources: docs/writing-tests/snapshot-testing.mdx
A balanced Storybook regression strategy uses each test type for its strongest signal. Visual tests cover appearance across stories and are the first choice for component-level UI changes. Interaction tests cover behavior that depends on user events, assertions, and rendered state transitions. Accessibility tests cover accessibility rules and assistive-technology expectations. Snapshot tests cover small pieces of DOM or HTML where textual structure is the contract. Before adding a snapshot, decide what a maintainer should learn from a future failure, and choose the test type that communicates that failure with the least noise.
Compact Reference
| Concern | Recommended Storybook approach | Practical note |
|---|---|---|
| Broad UI appearance regressions | Visual tests over stories | Best for reviewable changes in layout, styling, and component states. |
| Functional regressions | Interaction tests | Prefer assertions driven by user behavior when behavior is the contract. |
| DOM or HTML contracts | Portable Stories snapshot tests | Compose the story, render it in Vitest, Jest, or Playwright CT, then compare the snapshot. |
| Existing Storyshots suites | Migrate to Portable Stories | Storyshots is deprecated and no longer maintained. |
| Noisy snapshot diffs | Narrow or remove the snapshot | Prefer smaller contracts or visual review when serialized markup is too broad. |
Next Steps
Start by identifying the regression signal your component library lacks. Add visual coverage for important stories when the risk is appearance drift, then add interaction tests for user behavior and accessibility tests for inclusive usage requirements. Add snapshot tests only for focused output contracts where a textual diff will be useful during code review. For implementation details, continue to the Portable Stories documentation for Vitest, Jest, or Playwright Component Testing, and keep the story file as the single source of truth for the component state under test.