ScreenshotNeo

BlogHow-to

How to Visually Test React Components with Storybook

Use Storybook stories as visual test cases, compare screenshots with baselines, and add interaction tests where appearance checks are not enough.

By the ScreenshotNeo team4 October 20267 min read

To visually test React components with Storybook, create stories for the component states you want to protect, then capture those stories and compare the screenshots with a previous baseline. Storybook’s documented hosted workflow uses the official @chromatic-com/storybook addon with Chromatic. Add interaction tests when you also need to verify that a user action produces the right result: screenshot comparisons check appearance, while interaction assertions check behavior.

1. Model the component states you want to test

A Storybook story is an example of a component in a particular state and configuration. Treat each meaningful state as a potential visual test case: a default button, a disabled button, a form with validation errors, or a menu in its open state. Visual coverage depends on the stories you write; a component state that has no story will not be covered by these checks.

For example, a button component might have stories for its ordinary and disabled appearance. Adapt the component and imports to your project:

// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta = {
  title: 'Components/Button',
  component: Button,
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
  args: { children: 'Save changes' },
};

export const Disabled: Story = {
  args: { children: 'Save changes', disabled: true },
};

Stories should be stable and representative. Include states that are visually distinct or important to users, and provide deterministic data so a change in content or timing does not create irrelevant screenshot differences.

2. Choose a visual testing workflow

Storybook documents a hosted cross-browser path using Chromatic and the official @chromatic-com/storybook addon. The addon connects Storybook to the Visual Tests panel, where you can run checks and inspect visual changes. Storybook’s visual testing documentation says visual tests catch bugs in UI appearance, including changes in layout, color, size, and contrast.

Installation and compatibility instructions depend on the Storybook version. The cited Storybook 8 guide says Storybook 7.6 or later is required. Check the guide matching your installed version before copying the command or assuming compatibility.

  1. Open Storybook’s visual testing guide for your installed release.
  2. Use the version-matched Storybook CLI command to install @chromatic-com/storybook.
  3. Run Storybook and open the Visual Tests panel.
  4. Run visual checks for the stories you want covered.
  5. Review changed screenshots against their previous baselines. Accept a change only after deciding it is an intended design update.

Chromatic is described by Storybook as its cloud service for cross-browser visual testing. This hosted route is useful when you want cross-browser visual review. The documentation cited here does not establish a neutral cost or performance comparison against local options, so evaluate those against your project’s requirements.

3. Review screenshot changes carefully

A baseline is the earlier rendered screenshot used for comparison. When a new capture differs, review the changed area and decide whether it represents an intended design change or a regression. A changed screenshot is a signal for review, not proof that the component is broken.

  • Check story coverage: confirm that important variants, edge states, and responsive configurations have stories.
  • Inspect the changed region: identify whether the difference affects layout, color, size, contrast, or another visible detail.
  • Check the cause: consider intentional style edits, changed assets or fonts, content differences, and rendering conditions.
  • Update the baseline deliberately: accept it when the new appearance is intended; otherwise fix the component and rerun.

For reliable comparisons, keep story inputs and rendering conditions consistent. If a screenshot changes because its data or environment changed unexpectedly, stabilize that input before treating the difference as a product regression.

4. Add interaction tests for behavior

Visual checks answer whether a rendered state looks different from its baseline. They do not, by themselves, assert that a button works or that a user action produces the expected result. For important interactive flows, define a story with the intended initial state and a play function that simulates an action and asserts the outcome.

For example, a form story can start empty, simulate submitting it, and assert that validation feedback appears. Use the interaction utilities and assertion APIs supported by the Storybook version and test setup in your project; the example below illustrates the shape of the test rather than prescribing imports for every version:

// LoginForm.stories.tsx (illustrative; use APIs supported by your setup)
import type { Meta, StoryObj } from '@storybook/react';
import { LoginForm } from './LoginForm';

const meta = {
  title: 'Forms/LoginForm',
  component: LoginForm,
} satisfies Meta<typeof LoginForm>;

export default meta;
type Story = StoryObj<typeof meta>;

export const EmptySubmit: Story = {
  play: async ({ canvas, userEvent, expect }) => {
    await userEvent.click(canvas.getByRole('button', { name: /sign in/i }));
    await expect(canvas.getByText(/email is required/i)).toBeVisible();
  },
};

Storybook’s Vitest addon turns stories into tests. Its documentation recommends browser mode with Playwright Chromium for real-browser fidelity and says the addon requires a Vite-based Storybook framework. It also documents specific conditions for Next.js frameworks. Check the current compatibility guide for your Storybook version and framework before adopting it.

5. Combine visual and behavior coverage

A practical workflow is to use visual checks across representative story states and reserve interaction assertions for important user flows. Select the workflow based on the coverage you need:

Need Approach What to verify
Appearance regression coverage Visual tests on stories Story-state coverage, screenshot review, and baseline changes
Hosted cross-browser review Storybook’s documented visual addon and Chromatic workflow Version compatibility, browser coverage, review workflow, and team needs
Browser-based component or interaction tests Storybook Vitest addon Vite framework compatibility, browser mode setup, and assertions for user actions
Both appearance and behavior Visual checks plus interaction tests That important states render correctly and key actions produce expected outcomes

These workflows serve different purposes. A screenshot comparison identifies appearance changes; an interaction test checks behavior. Choose based on story-state coverage, browser coverage, framework compatibility, where the tests run, and how your team wants to review and debug results.

6. Troubleshoot common issues

Symptom Likely cause What to do
A visual change is reported after a code change The rendered appearance differs from the saved baseline, either intentionally or due to a regression. Inspect the changed area and the related story. Fix the component if unintended; update the baseline if the design change is intended.
An important state is missing from results There is no story for that state, or the state is not included in the run. Add a story for the state and ensure it is included in visual checks.
The visual addon setup does not match the project The guide or command may target a different Storybook release. Use the guide matching the installed version. The cited Storybook 8 guide states a 7.6 minimum; verify current requirements in the applicable guide.
The Vitest addon is incompatible with the framework The addon requires a Vite-based Storybook framework, and framework-specific requirements apply. Check the current compatibility documentation, including the documented Next.js conditions, before configuring it.
A screenshot changed but behavior still appears correct Visual checks report rendered appearance differences; they do not determine whether behavior is correct. Review the appearance change separately and add an interaction test for the behavior that matters.
An interaction assertion fails The simulated action, accessible query, expected result, or component state does not match the story. Verify the story’s initial state, target query, action, and expected outcome; keep behavior assertions focused on user-visible results.

7. Keep the workflow dependable and economical

Build confidence through representative stories, stable inputs, and deliberate baseline review. Visual checks can cover appearance across many component states, while interaction tests can focus on the flows where a behavioral failure matters. Storybook’s cited documentation describes the available workflows but does not provide a neutral cost or performance comparison, so estimate runtime and service cost using the configuration and usage that fit your project.

  • Keep story data and relevant rendering conditions deterministic.
  • Cover meaningful variants without creating redundant stories that add review work without improving state coverage.
  • Review visual changes before updating baselines.
  • Use interaction assertions where a screenshot alone cannot establish the expected outcome.
  • Check version and framework compatibility before upgrading or adding an addon.

Or skip the browser setup

If you need a screenshot of a page while documenting or inspecting a component, ScreenshotNeo provides a website screenshot API and MCP server. A single GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for parameters and configuration. For example, this cURL call saves a WebP capture:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Equivalent Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

Equivalent Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 screenshots. Sign up free for ScreenshotNeo.

FAQ

Does a visual test prove that a component works?

No. It checks rendered appearance against a baseline. Add interaction tests to assert actions and outcomes.

Do I need a story for every possible state?

Cover the states that matter to your users and the changes your team wants to catch. A state without a story is outside that story-based visual coverage.

Can I use Storybook’s Vitest addon with any React setup?

Do not assume so. The documented addon requires a Vite-based Storybook framework, and the docs specify additional framework conditions. Check compatibility for your setup and version.