How to Migrate from Older Storybook Versions: Complete Guide to Storybook 8

Storybook 8 requires Node.js 20.19 or 22.12, ESM-only configuration files, and the removal of the storiesOf API, alongside TypeScript module resolution changes and new addon package names.

Migrating from Storybook 7 (or earlier) to Storybook 8 involves significant architectural changes that affect configuration, module resolution, and story authoring. This guide covers the essential steps to upgrade your .storybook/main.ts, TypeScript settings, and addon imports based on the official storybookjs/storybook repository's next branch.

Upgrade Node.js to Version 20.19 or 22.12

Storybook 8 drops support for older Node runtimes. The minimum required version is Node.js 20.19 or 22.12.

Older Node versions cannot handle the new ESM import-meta resolution without experimental flags. Verify your version before proceeding:

node --version

Convert Configuration Files to ESM

All files inside the .storybook directory—including main.ts, preview.ts, and any custom preset files—must be valid ES modules. This means:

  • No require() statements
  • No __dirname or __filename without explicit construction
  • All relative imports require explicit file extensions (.js, .ts)

If you still need CommonJS compatibility, import createRequire from node:module.

Example: Converting .storybook/main.ts to ESM

// .storybook/main.ts
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

// Reconstruct __dirname for ESM compatibility
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

export default {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    resolve(__dirname, '../my-addon/index.js'),  // Explicit extension required
    '@storybook/addon-essentials',
  ],
  core: {
    // Builder must be fully resolved absolute path
    builder: resolve(__dirname, '../node_modules/@storybook/builder-vite'),
  },
};

Update TypeScript Module Resolution

Your tsconfig.json must use a moduleResolution setting that supports the types condition. Storybook 8 removed all typesVersions fields, so the old "node" resolution can no longer locate typings.

Set "moduleResolution" to "bundler", "node16", or "nodenext":

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

Resolve Builder Paths Explicitly

If you configure core.builder in .storybook/main.ts, it must now be a fully resolved absolute path. Relative paths or package name strings are no longer valid.

// ❌ Invalid
core: {
  builder: '@storybook/builder-vite',
}

// ✅ Valid
import { resolve } from 'node:path';
core: {
  builder: resolve(__dirname, '../node_modules/@storybook/builder-vite'),
  // Or using require.resolve if in a hybrid setup
  builder: require.resolve('@storybook/builder-vite'),
}

Migrate Addon APIs and Imports

The old @storybook/addons package has been split into two distinct packages:

  • @storybook/manager-api for manager UI configuration
  • @storybook/preview-api for preview (canvas) functionality

Legacy shim packages—including @storybook/channel-postmessage, @storybook/channel-websocket, @storybook/client-api, @storybook/store, and @storybook/api—have been merged into these new APIs.

Updating addons.setConfig

// Before (Storybook 7)
import { addons } from '@storybook/addons';
addons.setConfig({ theme: myTheme });

// After (Storybook 8)
import { addons } from '@storybook/manager-api';
addons.setConfig({ theme: myTheme });

Remove storiesOf and Adopt CSF

The storiesOf API has been completely removed. You must migrate all stories to Component Story Format (CSF). For dynamic story generation, use the experimental experimental_indexers API.

Migration Example

// ❌ Removed API
import { storiesOf } from '@storybook/react';
storiesOf('Button', module)
  .add('Primary', () => <Button primary>Click me</Button>);

// ✅ CSF 3.0 (Recommended)
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta: Meta<typeof Button> = {
  title: 'Button',
  component: Button,
};
export default meta;

type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
    primary: true,
    children: 'Click me',
  },
};

Update Testing Utilities

The @storybook/testing-library package is deprecated. All testing utilities have moved to @storybook/test.

// Before
import { userEvent } from '@storybook/testing-library';

// After
import { userEvent } from '@storybook/test';

Configure Vite Plugins Explicitly

Framework-specific Vite plugins are no longer auto-added by Storybook. You must import and configure them explicitly in your vite.config.ts.

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
});

If using .storybook/main.ts with viteFinal, ensure you preserve existing plugins:

// .storybook/main.ts
import react from '@vitejs/plugin-react';

export default {
  viteFinal: async (config) => ({
    ...config,
    plugins: [react(), ...(config.plugins ?? [])],
  }),
};

Adjust Portable Stories for Testing

The composeStory and composeStories functions have changed behavior:

  • Project annotations are now merged rather than overwritten
  • The play function may return undefined
  • Vue 3: Returns a component instance instead of a render function

Vue 3 Portable Stories Example

// Before (Returns function)
import { composeStory } from '@storybook/vue3';
const Primary = composeStory(stories.Primary, stories.default);
render(Primary({ foo: 'bar' })); // ❌

// After (Returns component)
import { render } from '@testing-library/vue';
const Primary = composeStory(stories.Primary, stories.default);
render(Primary, { props: { foo: 'bar' } }); // ✅

UI and Runtime Changes

The Manager UI now runs on React 18, which affects any custom addon UI components. Default keyboard shortcuts have changed, and system tags (docs, story) have been removed from the UI.

Review any custom shortcut overrides or addon UI code for React 18 compatibility.

Summary

  • Node.js 20.19+ is required for ESM support
  • Convert .storybook/main.ts and presets to ESM with explicit file extensions
  • Update tsconfig.json to use "moduleResolution": "bundler" (or node16/nodenext)
  • Set core.builder to a fully resolved absolute path
  • Replace @storybook/addons with @storybook/manager-api and @storybook/preview-api
  • Remove all storiesOf usage in favor of CSF 3.0
  • Switch from @storybook/testing-library to @storybook/test
  • Add explicit Vite plugins in vite.config.ts
  • Handle composeStory returning components (not functions) in Vue 3

Frequently Asked Questions

What Node.js version is required for Storybook 8?

Storybook 8 requires Node.js 20.19 or 22.12 minimum. Earlier versions lack native support for the ESM import-meta resolution features that Storybook 8 relies on for configuration loading.

Do I have to convert my Storybook config to ESM?

Yes. Files in the .storybook directory—including main.ts, preview.ts, and any custom presets—must be valid ES modules. You cannot use require() or implicit file extensions in relative imports. Use import { createRequire } from 'node:module' if you need to import CommonJS modules.

What happened to the storiesOf API?

The storiesOf API has been completely removed in Storybook 8. You must rewrite any stories using this API to the Component Story Format (CSF). For dynamic story generation scenarios, use the experimental_indexers API instead.

Why am I getting TypeScript errors after upgrading?

Storybook 8 removed all typesVersions fields from its packages. Your tsconfig.json must use "moduleResolution": "bundler", "node16", or "nodenext" to properly resolve type definitions. The legacy "node" resolution cannot locate the new typings.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →