EmpireUI
Get Pro
← Blog8 min read#storybook#component-docs#chromatic

Storybook 8: Documentation, Testing, and Chromatic Integration

Storybook 8 ships faster builds, a revamped test runner, and tighter Chromatic integration. Here's what actually changed and how to wire it into your component library.

Developer writing component documentation on a dark-themed code editor with UI component previews visible on a second monitor

What Actually Changed in Storybook 8

Honestly, Storybook 8 is the first release in years that made me want to migrate the same week it dropped — not after six months of waiting for the ecosystem to catch up.

The headline feature is a fully rewritten Vite-first bundler pipeline. In Storybook 7, Vite support felt bolted on. Now it's the default for new projects, and startup time on a mid-sized component library dropped from ~22 seconds to under 6 in my tests. Webpack is still supported via @storybook/webpack5, but you'll need to opt in explicitly.

The other big shift is how stories interact with the test runner. Storybook 8 ships @storybook/test as a first-party package that wraps Vitest under the hood. That means you write your play functions using @storybook/test imports instead of @storybook/testing-library, and you get native Vitest globals without extra config.

Controls got smarter too. The autodocs template now infers TypeScript types directly from your component props via react-docgen-typescript 2.3, so you're not manually annotating argTypes for every string union. It's not perfect — complex generics still need hand-holding — but it eliminates the boilerplate for 80% of components.

Setting Up Storybook 8 in a React + Tailwind Project

If you're starting fresh, the init command handles most of it. For existing projects, the migration CLI is genuinely useful this time — it patched my main.ts, updated imports, and only left two things for me to fix manually.

Here's a minimal main.ts for a React + Tailwind v4.0.2 project. The key addition is viteFinal to inject your Tailwind plugin into the Vite config that Storybook uses internally:

import type { StorybookConfig } from '@storybook/react-vite';
import tailwindcss from '@tailwindcss/vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(ts|tsx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-a11y',
    '@chromatic-com/storybook',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  viteFinal: async (config) => {
    config.plugins = [...(config.plugins ?? []), tailwindcss()];
    return config;
  },
};

export default config;

One gotcha: if you're using CSS Modules alongside Tailwind, make sure your preview.ts imports your global stylesheet before any story-level imports. Order matters because Vite processes them in import order, and mismatched specificity causes styles to bleed between stories in ways that are annoying to debug.

Writing Stories That Double as Tests

The play function API hasn't changed structurally, but the imports have. Everything now comes from @storybook/testuserEvent, expect, within, all of it. The benefit is that these same assertions run inside the Storybook UI via the interactions panel AND inside your CI test runner without any extra setup.

Here's a button story with a play function that tests focus behavior — the kind of thing that's easy to miss in unit tests but critical for WCAG accessibility compliance:

import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  component: Button,
  tags: ['autodocs'],
};
export default meta;

type Story = StoryObj<typeof Button>;

export const FocusVisible: Story = {
  args: { label: 'Click me', variant: 'primary' },
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    const button = canvas.getByRole('button');
    await userEvent.tab();
    await expect(button).toHaveFocus();
    await expect(button).toHaveStyle({
      outlineOffset: '2px',
    });
  },
};

The tags: ['autodocs'] line on the meta object is what triggers automatic documentation generation. Storybook 8 moved away from the parameters.docs.page approach entirely. If you've got old stories using that pattern, the migration CLI catches most of them but double-check any custom docs pages.

Chromatic Integration: Visual Regression on Every PR

Chromatic is built by the same team as Storybook, and in version 8 the integration is tight enough that it feels like a first-party feature. The @chromatic-com/storybook addon replaces the older chromatic package and adds a dedicated sidebar panel showing your snapshot history inline.

Setup is three steps: install the addon, add your project token to CI, and push. Chromatic captures a screenshot of every story on every PR and diffs them against the accepted baseline. For a component library with 40 visual styles — like Empire UI's design token system — this catches regressions that would otherwise slip through code review entirely.

The --only-changed flag is worth knowing about. It uses git to figure out which stories are affected by a given diff and only runs snapshots for those. On a large library this cuts Chromatic build time significantly. You'll want to pair it with --externals to tell Chromatic about files that affect all components (your global CSS, design tokens, theme config) so it doesn't skip snapshots when those change.

Does visual regression testing replace a proper color system review? No. But it does mean that when you tweak rgba(255,255,255,0.15) on a glassmorphism surface and accidentally break contrast on five other components, you find out before it ships rather than after a user files a bug.

Autodocs: Getting Useful Documentation Without Busywork

The autodocs template in Storybook 8 generates a docs page for every component tagged with 'autodocs'. It pulls prop descriptions from JSDoc comments on your TypeScript interfaces, renders an interactive controls table, and shows your named stories as live examples. That's a solid baseline.

Where it falls short is complex components — anything with compound patterns, slot-based composition, or context dependencies. For those you'll want an MDX story file. MDX support in Storybook 8 now uses MDX 3, which means you can use ES module imports and async components inside your docs. If you're building out a proper component library with Storybook, the MDX approach gives you the narrative documentation that autodocs can't.

One pattern I've found useful: write the autodocs story file for props documentation, then write a separate MDX file that imports those stories and adds context — design decisions, accessibility notes, when to use this component versus that one. Storybook renders both. Engineers get the props table, designers get the rationale.

The Test Runner in CI

The @storybook/test-runner package runs all your play functions headlessly via Playwright. In Storybook 8 it's stable enough to trust in CI — earlier versions had flakiness issues with async rendering that required manual waitFor wrappers everywhere.

A minimal GitHub Actions step looks like this:

- name: Run Storybook tests
  run: |
    npx concurrently -k -s first -n "SB,TEST" \
      "npx storybook dev -p 6006 --no-open" \
      "npx wait-on tcp:6006 && npx test-storybook"
  env:
    STORYBOOK_BASE_URL: http://localhost:6006

The wait-on step is important — without it your test runner tries to connect before Storybook finishes starting and you get confusing timeout errors. Set STORYBOOK_CONCURRENCY to something like 4 in CI to avoid hammering the server with parallel requests on machines with limited CPU.

If you're also running Chromatic in CI, you don't need to run the test runner separately. Chromatic's new --run-tests flag executes play functions as part of the snapshot run. One less job to maintain.

Theming Storybook to Match Your Design System

Storybook's own UI is themeable via @storybook/theming. For a library with a dark mode — or a theme toggle in React — you'll want the Storybook UI to match so screenshots look right and demos feel cohesive.

The preview.ts file is where you control the story canvas background. For a component library supporting multiple themes, the backgrounds addon lets you define named backgrounds that reviewers can switch between. Set your design token values directly rather than using CSS variables — Storybook's addon doesn't pick up CSS custom properties defined on :root.

For Empire UI specifically, we define a glassmorphism background with rgba(15, 15, 25, 0.95) so the frosted glass components render against a realistic dark surface. Without that, the visual regression snapshots are meaningless because the backdrop is part of what glassmorphism actually is as a visual effect.

Storybook 8 and the Broader Documentation Ecosystem

Storybook 8 doesn't try to replace your external docs site. It's a development and testing tool that happens to produce readable documentation as a side effect. For user-facing docs you'll still want something like Docusaurus, Fumadocs, or Nextra — places where you control the full layout, can write long-form guides, and can embed Storybook iframes for live demos.

The pattern that works well: use Storybook for component-level docs and visual testing, use your docs site for system-level documentation — spacing systems, icon guidelines, Figma-to-React workflows. They serve different audiences at different stages of the workflow.

What's the right mental model? Think of Storybook as the source of truth for component behavior and appearance. Your docs site is the editorial layer on top. Keep them in sync by importing story data into your docs site via the Storybook data API rather than duplicating content manually.

The ecosystem around Storybook 8 is maturing fast. The Vite integration is solid, the test runner is production-ready, and Chromatic's visual regression workflow is genuinely useful. If you've been putting off setting this up because the previous versions felt fragile, version 8 is worth a proper look.

FAQ

Can I use Storybook 8 with Webpack instead of Vite?

Yes. Install @storybook/webpack5 and set framework.name to @storybook/react-webpack5 in your main.ts. Vite is the default for new projects but Webpack support is maintained. Build times will be slower — on a comparable project, Vite cold starts in under 6 seconds versus 20+ for Webpack.

What's the difference between @storybook/test and @testing-library/react in Storybook 8?

@storybook/test re-exports everything from @testing-library/react and @testing-library/user-event, plus Vitest's expect. It's the recommended import for Storybook 8 play functions because the same assertions run in the browser panel and in the headless test runner without config changes.

How do I prevent Chromatic from re-snapshotting every story when I change a shared utility file?

Use the --externals flag to list files that affect all stories (global CSS, token files, theme config). Chromatic uses git diffing with --only-changed to limit snapshots, but it won't know a utility change is global unless you tell it. Example: chromatic --only-changed --externals src/tokens/*.css.

My autodocs page isn't picking up TypeScript prop descriptions. What's wrong?

Check that react-docgen-typescript is installed and that your tsconfig.json paths are correctly resolved by Vite. JSDoc comments must be on the interface or type definition, not on the component function itself. Also confirm the component is exported as a named export — default exports sometimes confuse the docgen parser.

Can I run Storybook tests without a running Storybook dev server?

Yes, with --url pointing to a static build. Run storybook build to output to storybook-static/, serve it with npx serve storybook-static -p 6006, then run test-storybook --url http://localhost:6006. This is faster in CI because you build once and run tests without the dev server overhead.

Does Storybook 8 support React Server Components?

Partially. You can render RSC output in stories by wrapping components in a client boundary, but Storybook doesn't execute server-side data fetching. The recommended approach is to mock async data in your story args and test the presentational layer. Full RSC support is on the roadmap but not in the 8.x releases.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Testing a Design System: Visual, Unit, and Accessibility TestsBuilding Design Systems That Scale: Engineering Guide 2026Chromatic Visual Testing: Catch UI Regressions Before They ShipComponent Testing in 2027: Visual, Unit, Interaction Compared