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
__dirnameor__filenamewithout 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-apifor manager UI configuration@storybook/preview-apifor 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
playfunction may returnundefined - 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.tsand presets to ESM with explicit file extensions - Update
tsconfig.jsonto use"moduleResolution": "bundler"(ornode16/nodenext) - Set
core.builderto a fully resolved absolute path - Replace
@storybook/addonswith@storybook/manager-apiand@storybook/preview-api - Remove all
storiesOfusage in favor of CSF 3.0 - Switch from
@storybook/testing-libraryto@storybook/test - Add explicit Vite plugins in
vite.config.ts - Handle
composeStoryreturning 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →