Storybook UI with Vue component stories in a development workspace
7 min readVue 3 Migration

Storybook 8+ with Vue 3: Turning Your Component Library Into a Migration Safety Net

The fastest way to lose a migration is to discover UI regressions only in production. Storybook in front of a Vue 2→3 move is the isolation layer: each story is a contract for props, slots, and events before the application router, auth, and data join the conversation. It pairs naturally with design-system work and with Cypress component tests for critical widgets.

Upgrade Storybook and Vue in the same branch the library uses. A green Storybook on old Vue 3 beta stacks does not help when the app is on 3.5.x.

1. Stories for edge cases

Empty, loading, error, and “too much data” states. Those are the states that break when reactivity and slot forwarding change.

2. Addons that pay rent

a11y and interactions first; avoid addon sprawl. Your goal is faster feedback, not a second product to maintain.

3. Connect to the rest of the test stack

Chromatic or similar for visual diffs, or a lighter snapshot setup if budget is tight. The point is signal when class names and layout change, not only when JS throws.

4. A minimal Storybook 8 config for Vue 3

Storybook 8 with the Vite-Vue 3 framework is the path of least resistance. Keep .storybook/ small—your goal is fast feedback, not a configuration shrine. The two files below are enough to start.

// .storybook/main.ts
import type { StorybookConfig } from '@storybook/vue3-vite'

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(ts|js)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-a11y',
    '@storybook/addon-interactions'
  ],
  framework: { name: '@storybook/vue3-vite', options: {} },
  docs: { autodocs: 'tag' }
}
export default config
// src/components/InvoiceRow.stories.ts
import type { Meta, StoryObj } from '@storybook/vue3'
import InvoiceRow from './InvoiceRow.vue'

const meta: Meta<typeof InvoiceRow> = {
  component: InvoiceRow,
  tags: ['autodocs'],
  args: { invoice: { id: 'INV-1', amount: 1200, status: 'open' } }
}
export default meta

export const Default: StoryObj<typeof InvoiceRow> = {}
export const Overdue: StoryObj<typeof InvoiceRow> = {
  args: { invoice: { id: 'INV-2', amount: 980, status: 'overdue' } }
}
export const Empty: StoryObj<typeof InvoiceRow> = {
  args: { invoice: null }
}

Each story is a contract: props in, visible state out. When you migrate InvoiceRow from Options API to script setup, those stories tell you—visually and via Volar—whether the contract held. Pair with our incremental TypeScript approach and the stories double as type-checked usage examples.

5. Stories as migration contracts

Treat each story as a behavior fence around a component. Before touching the source for Vue 3, capture five canonical stories: default, loading, error, empty, and overflow. Snapshot them. Migrate. Compare. The diff that matters is visual, not unit-test green.

  • Default: the “happy path” the designer keeps in their head.
  • Loading: spinners, skeletons, and the timing of when content swaps in.
  • Error: network 5xx, validation failure, permission denied.
  • Empty: no data, first-time-user, or filtered-to-zero.
  • Overflow: 200-character names, 50-row tables, RTL locales—the states that break flex/grid in subtle ways.

If your component library covers those five for the top 30 components, the application-level Cypress component tests get to be much smaller and more focused on integration concerns.

6. Visual regression: where it pays off

Vue 3’s render function changes, scoped slot semantics, and class merging behaviors all shift the rendered HTML in ways that unit tests do not see. Visual regression on the Storybook URL is the cheapest insurance you can buy. Even a small set of stories run through Chromatic, Playwright screenshots, or a homegrown snapshot diff catches:

  • Class merging differences (Vue 3 merges class on root nodes differently from Vue 2 in some cases).
  • Scoped slot fallback content rendering when a slot is unused.
  • Transition group keying changes that cause layout shifts.
  • Vuetify/Element UI library upgrades that quietly bump padding by 2px everywhere.

Run visual regression on PRs that touch design tokens, on framework-version bumps, and after any third-party UI library upgrade. Don’t run it on every PR—snapshot fatigue kills adoption.

7. Anti-patterns we see

  • Storybook as a documentation site only. If stories aren’t executed in CI, they rot. Wire them into the same pipeline as your unit tests.
  • Mocking the entire backend in decorators. A story should describe a component, not an integration. Push backend concerns to MSW or fixtures.
  • Addon hoarding. Each addon is a future migration burden. essentials, a11y, and interactions are usually enough.
  • One mega-story with 40 controls. Stories are screenshots, not playgrounds. Keep them small and named.
  • Storybook upgrade on a separate timeline. Storybook 8 in the same branch as Vue 3, please. A green Storybook on Vue 2 is theater.

8. FAQ

Do we need Storybook if we use Vue Test Utils?

They solve different problems. Vue Test Utils proves logic; Storybook proves visuals and UX states. Most teams need both during a migration—see our testing strategy guide.

Can we run Storybook on Vue 2 and Vue 3 at the same time?

Yes—two Storybook instances, one per branch of the codebase. It’s the cleanest way to compare components side by side during a strangler migration.

What about Histoire instead of Storybook?

Histoire is lighter and Vue-native. If your team has zero existing Storybook investment, it’s a reasonable choice. If you already run Storybook 8 in CI, the migration cost rarely justifies the switch.

Where does Storybook fit on the budget conversation?

It’s usually folded into the test infrastructure line of the migration cost estimate. Stories that already exist make the estimate go down.

9. Storybook safety-net checklist

  • Storybook 8+ on the same Vue 3 version as the app.
  • Top 30 components have default/loading/error/empty/overflow stories.
  • Visual regression runs on framework bumps and design-token PRs.
  • a11y addon flags WCAG violations and blocks PRs above a threshold.
  • Stories deployed (Chromatic, Vercel, S3) so designers can review without checking out branches.
  • Storybook build runs in the same CI as the app build—shared cache where possible.
  • Stories live next to components, not in a separate /stories graveyard.

A Storybook that fails this checklist isn’t a safety net—it’s a museum. Fund the gaps before you begin the next strangler vine.

Conclusion

Storybook does not replace E2E; it shrinks the E2E surface. Fund stories early in the program and the Vue 3 app integration phase gets boring—in a good way.

Related