Back to Blog

Visual Testing for Storybook: Catching Component Regressions Across Every Variant

Storybook is the de-facto component workshop for React, Vue, and Svelte teams. It catalogs every variant of every component in one place - which makes it the perfect target for visual regression testing. The problem: Storybook itself does not check for visual changes. You still need a screenshot tool that snapshots each story and diffs it against a baseline.

This post walks through wiring Lastest to Storybook so that every story, every variant, and every controlled state becomes a tracked baseline - at zero per-snapshot cost.

storybook · Button Components ▾ Button Primary Secondary Disabled Loading Hover Icon-only ▸ Card ▸ Modal ▸ Toast ▸ Tabs Button - 6 stories Primary Click me Secondary Click me Disabled Click me Loading Hover diff Icon-only 5 of 6 baselines stable · 1 visual diff pending review Approve all Compare
Story tree on the left, variant grid on the right. One variant flagged for visual diff review.

Why Storybook needs a visual layer

Storybook gives you isolation: every variant lives in its own controlled environment, free of layout interference from the rest of the app. That same isolation is what makes it the cleanest possible target for visual regression testing. A single story renders deterministically, in the same viewport, with the same controls - exactly the conditions you want for screenshot diffing.

Without a visual layer, Storybook tells you what your components are. With one, it tells you when they change.

How Lastest connects to Storybook

Lastest reads your built Storybook (the static storybook-static output) and treats every story URL as a test target. You point the runner at the directory or hosted URL, and it walks iframe.html?id=… for each story.

lastest storybook-scan \
  --storybook-url http://localhost:6006 \
  --output suite.json

That command produces a suite where each story becomes a tracked test. From there, every CI run snapshots every story and compares against the baseline.

Pipeline from build-storybook to storybook-scan to snapshot-and-diff with three engines, then one human review verdict that updates the baseline at zero per-snapshot fees
One CI pass turns every Storybook story into a tracked baseline, with a single human review seam and no per-snapshot charges.

Capture every variant, not just the happy path

The biggest win is coverage. Most teams write Cypress or Playwright tests for the primary button. Almost nobody covers the disabled, loading, hover, focus, and icon-only variants. Storybook already documents them - visual testing now guards them.

Two-column comparison: Storybook alone covers only the primary variant, one theme, and lets states slip through; Storybook plus Lastest tracks every story, every state, and the full theme matrix
Storybook documents every variant, but only adding Lastest actually guards the disabled, loading, hover, and full-theme states.

Three patterns we recommend:

  • Use Controls to fan out states - generate a story per meaningful prop combination instead of relying on a single "all variants" page.
  • Pin stories deterministically - freeze dates, randomness, and animation start times in your preview.ts. Visual tests are only as stable as the story.
  • Group by component - Lastest lets you organize tests into suites that mirror your story tree, so a regression in Button/* shows up as one collapsible group instead of six unrelated failures.

Theme and viewport coverage

Storybook addons like @storybook/addon-themes and @storybook/addon-viewport let you switch presentation. Lastest reads those args and captures a snapshot per combination. Pair that with three diff engines (pixel, structural, perceptual) and you can use perceptual diffs for the full theme matrix and pixel-perfect diffs for individual icon stories.

PR #482 · visual regression report 42 stories captured · 41 passed · 1 needs review · 0 errors Button (6) 5 pass 1 review Card (4) 4 pass Modal (8) 8 pass Toast (3) 3 pass Open dashboard
CI report grouped by component. One variant flagged needs review; everything else green.

CI workflow

Build Storybook, hand the static directory to Lastest, run the suite, post a PR comment with the diff link.

- run: pnpm build-storybook
- run: lastest storybook-scan --static-dir storybook-static --output suite.json
- uses: las-team/lastest/action@main
  with:
    suite: suite.json
    server-url: ${{ secrets.LASTEST_SERVER_URL }}
    runner-token: ${{ secrets.LASTEST_RUNNER_TOKEN }}

No per-snapshot quota. No story-count tier. Every variant gets the same treatment as the happy path - which is the only way visual testing actually catches the bugs that ship.