Skip to content

Instantly share code, notes, and snippets.

@valentinpalkovic
Last active August 22, 2025 09:40
Show Gist options
  • Select an option

  • Save valentinpalkovic/c5e19b8a46b6683a91b8306574d713df to your computer and use it in GitHub Desktop.

Select an option

Save valentinpalkovic/c5e19b8a46b6683a91b8306574d713df to your computer and use it in GitHub Desktop.
Storybook 9 Migration Docs and Features LLM Gist
### Core Changes and Removals
#### Dropped support for legacy packages
The following packages are no longer published as part of `9.0.0`:
The following packages have been consolidated into the main `storybook` package:
| Old Package | New Path |
| ------------------------------- | ----------------------- |
| `@storybook/manager-api` | `storybook/manager-api` |
| `@storybook/preview-api` | `storybook/preview-api` |
| `@storybook/theming` | `storybook/theming` |
| `@storybook/test` | `storybook/test` |
| `@storybook/addon-actions` | `storybook/actions` |
| `@storybook/addon-backgrounds` | N/A |
| `@storybook/addon-controls` | N/A |
| `@storybook/addon-highlight` | `storybook/highlight` |
| `@storybook/addon-interactions` | N/A |
| `@storybook/addon-measure` | N/A |
| `@storybook/addon-outline` | N/A |
| `@storybook/addon-toolbars` | N/A |
| `@storybook/addon-viewport` | `storybook/viewport` |
Please un-install these packages, and ensure you have the `storybook` package installed.
Replace any imports with the path listed in the second column.
Additionally the following packages were also consolidated and placed under a `/internal` sub-path, to indicate they are for internal usage only.
If you're depending on these packages, they will continue to work for `9.0`, but they will likely be removed in `10.0`.
| Old Package | New Path |
| ---------------------------- | ------------------------------------ |
| `@storybook/channels` | `storybook/internal/channels` |
| `@storybook/client-logger` | `storybook/internal/client-logger` |
| `@storybook/core-common` | `storybook/internal/common` |
| `@storybook/core-events` | `storybook/internal/core-events` |
| `@storybook/csf-tools` | `storybook/internal/csf-tools` |
| `@storybook/docs-tools` | `storybook/internal/docs-tools` |
| `@storybook/node-logger` | `storybook/internal/node-logger` |
| `@storybook/router` | `storybook/internal/router` |
| `@storybook/telemetry` | `storybook/internal/telemetry` |
| `@storybook/types` | `storybook/internal/types` |
| `@storybook/manager` | `storybook/internal/manager` |
| `@storybook/preview` | `storybook/internal/preview` |
| `@storybook/core-server` | `storybook/internal/core-server` |
| `@storybook/builder-manager` | `storybook/internal/builder-manager` |
| `@storybook/components` | `storybook/internal/components` |
Addon authors may continue to use the internal packages, there is currently not yet any replacement.
```bash
npm uninstall @storybook/experimental-addon-test
npm install --save-dev @storybook/addon-vitest
```
Update your imports in any custom configuration or test files:
```diff
- import { ... } from '@storybook/experimental-addon-test';
+ import { ... } from '@storybook/addon-vitest';
```
If you're using the addon in your Storybook configuration, update your `.storybook/main.js` or `.storybook/main.ts`:
```diff
export default {
addons: [
- '@storybook/experimental-addon-test',
+ '@storybook/addon-vitest',
],
};
```
The public API remains the same, so no additional changes should be needed in your test files or configuration.
Additionally, we have deprecated the usage of `withActions` from `@storybook/addon-actions` and we will remove it in Storybook v10. Please file an issue if you need this API.
#### Dropped support
##### Vite 4
Storybook 9.0 drops support for Vite 4. The minimum supported version is now Vite 5.0.0. This change affects all Vite-based frameworks and builders:
- `@storybook/builder-vite`
- `@storybook/react-vite`
- `@storybook/vue-vite`
- `@storybook/vue3-vite`
- `@storybook/svelte-vite`
- `@storybook/web-components-vite`
- `@storybook/preact-vite`
- `@storybook/html-vite`
- `@storybook/experimental-nextjs-vite`
To upgrade:
1. Update your project's Vite version to 5.0.0 or higher
2. Update your Storybook configuration to use Vite 5:
```js
// vite.config.js or vite.config.ts
export default {
// ... your other config
// Make sure you're using Vite 5 compatible plugins
};
```
If you're using framework-specific Vite plugins, ensure they are compatible with Vite 5:
- `@vitejs/plugin-react`
- `@vitejs/plugin-vue`
- `@sveltejs/vite-plugin-svelte`
- etc.
For more information on upgrading to Vite 5, see the [Vite Migration Guide](https://vitejs.dev/guide/migration).
##### TypeScript < 4.9
Storybook now requires TypeScript 4.9 or later.
##### Node.js < 20
Storybook now requires Node.js 20 or later.
##### Package Managers
Minimum supported versions:
npm v10+
yarn v4+
pnpm v9+
While Storybook may still work with older versions, we recommend upgrading to the latest supported versions for the best experience and to ensure compatibility.
#### Moving from renderer-based to framework-based configuration
Storybook is moving from renderer-based to framework-based configuration. This means you should:
1. Update your source files to use framework-specific imports instead of renderer imports
2. Remove the renderer packages from your package.json
For example, if you're using `@storybook/react` with `@storybook/react-vite`, you should:
- Import types and functions from `@storybook/react-vite` instead of `@storybook/react`
- Remove `@storybook/react` from your package.json dependencies
```diff
- import { Meta, StoryObj } from '@storybook/react';
+ import { Meta, StoryObj } from '@storybook/react-vite';
```
### Addon-specific Changes
#### Essentials Addon: Viewport, Controls, Interactions and Actions moved to core
The `@storybook/addon-essentials` package has been removed. The viewport, controls, interactions and actions addons have been moved from their respective packages (`@storybook/addon-viewport`, `@storybook/addon-controls`, `@storybook/addon-interactions`, `@storybook/addon-actions`) to Storybook core. You no longer need to install these separately or include them in your addons list.
If you have used `@storybook/addon-docs` as part of essentials, you need to manually install it:
```bash
$ npx storybook add @storybook/addon-docs
```
#### A11y Addon: Removed deprecated manual parameter
The deprecated `manual` parameter from the A11y addon's parameters has been removed. Instead, use the `globals.a11y.manual` setting to control manual mode. For example:
```js
// Old way (no longer works)
export const MyStory = {
parameters: {
a11y: {
manual: true
}
}
};
// New way
export const MyStory = {
parameters: {
a11y: {
// other a11y parameters
}
}
globals: {
a11y: {
manual: true
}
}
};
// To enable manual mode globally, use .storybook/preview.js:
export const initialGlobals = {
a11y: {
manual: true
}
};
```
#### A11y Addon: Replace `element` parameter with `context` parameter
The `element` parameter from the A11y addon's parameters has been removed in favor of a new `context` parameter. The `element` parameter could be used with a single CSS selector string to configure which element to target with axe. The new `context` parameter supports the full range that `axe-core`'s Context API supports, _including_ a single selector like the removed `element` parameter did.
`context` does _not_ support passing in a `Node` or `NodeList` (like `document.getElementById('my-target')`).
```diff
export const MyStory = {
parameters: {
a11y: {
- element: '#my-target'
+ context: '#my-target'
}
}
};
```
#### Experimental Test Addon: Stabilized and renamed
In Storybook 9.0, we've officially stabilized the Test addon. The package has been renamed from `@storybook/experimental-addon-test` to `@storybook/addon-vitest`, reflecting its production-ready status. If you were using the experimental addon, you'll need to update your dependencies and imports.
The vitest addon automatically loads Storybook's `beforeAll` hook, so that you can remove the following line in your vitest.setup.ts file:
```diff
// .storybook/vitest.setup.ts
import { setProjectAnnotations } from '@storybook/react-vite';
import * as addonAnnotations from 'my-addon/preview';
import * as previewAnnotations from './.storybook/preview';
- const project = setProjectAnnotations([previewAnnotations, addonAnnotations]);
+ setProjectAnnotations([previewAnnotations, addonAnnotations]);
// the vitest addon automatically loads beforeAll
- beforeAll(project.beforeAll);
```
#### Vitest Addon (former @storybook/experimental-addon-test): Vitest 2.0 support is dropped
The Storybook Test addon now only supports Vitest 3.0 and higher, which is where browser mode was made into a stable state. Please upgrade to Vitest 3.0.
#### Viewport/Backgrounds Addon synchronized configuration and `globals` usage
The feature flags: `viewportStoryGlobals` and `backgroundsStoryGlobals` have been removed, please remove these from your `.storybook/main.ts` file.
See here for the ways you have to configure addon viewports & backgrounds:
- [New parameters format for addon backgrounds](#new-parameters-format-for-addon-backgrounds)
- [New parameters format for addon viewport](#new-parameters-format-for-addon-viewport)
#### Storysource Addon removed
The `@storybook/addon-storysource` addon and the `@storybook/source-loader` package are removed in Storybook 9.0. Instead, Storybook now provides a Code Panel via `@storybook/addon-docs` that offers similar functionality with improved integration and performance.
#### Mdx-gfm Addon removed
The `@storybook/addon-mdx-gfm` addon is removed in Storybook 9.0 since it is no longer needed.
**Migration Steps:**
1. Remove the old addon
Remove `@storybook/addon-storysource` from your project:
```bash
npx storybook remove @storybook/addon-storysource
```
2. Enable the Code Panel
The Code Panel can be enabled by adding the following parameter to your stories or globally in `.storybook/preview.js`:
```js
export const parameters = {
docs: {
codePanel: true,
},
};
```
Or for individual stories:
```js
export const MyStory = {
parameters: {
docs: {
codePanel: true,
},
},
};
```
### API and Component Changes
#### Button Component API Changes
The Button component has been updated to use a more modern props API. The following props have been removed:
- `isLink`
- `primary`
- `secondary`
- `tertiary`
- `gray`
- `inForm`
- `small`
- `outline`
- `containsIcon`
Use the new `variant` and `size` props instead:
```diff
- <Button primary small>Click me</Button>
+ <Button variant="primary" size="small">Click me</Button>
```
#### Icon System Updates
Several icon-related exports have been removed:
- `IconButtonSkeleton`
- `Icons`
- `Symbols`
- Legacy icon exports
Use the new icon system from `@storybook/icons` instead:
```diff
- import { Icons, IconButtonSkeleton } from '@storybook/components';
+ import { ZoomIcon } from '@storybook/icons';
```
#### Sidebar Component Changes
1. The 'extra' prop has been removed from the Sidebar's Heading component
2. Experimental sidebar features have been removed:
- `experimental_SIDEBAR_BOTTOM`
- `experimental_SIDEBAR_TOP`
#### Story Store API Changes
Several deprecated methods have been removed from the StoryStore:
- `getSetStoriesPayload`
- `getStoriesJsonData`
- `raw`
- `fromId`
#### Global State Management
The `globals` field in project annotations has been renamed to `initialGlobals`:
```diff
export const preview = {
- globals: {
+ initialGlobals: {
theme: 'light'
}
};
```
Additionally loading the defaultValue from `globalTypes` isn't supported anymore. Use `initialGlobals` instead to define the defaultValue.
```diff
// .storybook/preview.js
export default {
+ initialGlobals: {
+ locale: 'en'
+ },
globalTypes: {
locale: {
description: 'Locale for components',
- defaultValue: 'en',
toolbar: {
title: 'Locale',
icon: 'circlehollow',
items: ['es', 'en'],
},
},
},
}
```
#### Experimental Status API has turned into a Status Store
The experimental status API previously available at `api.experimental_updateStatus` and `api.getCurrentStoryStatus` has changed, to a store that works both on the server, in the manager and in the preview.
You can use the new Status Store by importing `experimental_getStatusStore` from either `storybook/internal/core-server`, `storybook/manager-api` or `storybook/preview-api`:
```diff
+ import { experimental_getStatusStore } from 'storybook/manager-api';
+ import { StatusValue } from 'storybook/internal/types';
+ const myStatusStore = experimental_getStatusStore(MY_ADDON_ID);
addons.register(MY_ADDON_ID, (api) => {
- api.experimental_updateStatus({
- someStoryId: {
- status: 'success',
- title: 'Component tests',
- description: 'Works!',
- }
- });
+ myStatusStore.set([{
+ value: StatusValue.SUCCESS
+ title: 'Component tests',
+ description: 'Works!',
+ }]);
```
#### `experimental_afterEach` has been stabilized
The experimental_afterEach hook has been promoted to a stable API and renamed to afterEach.
To migrate, simply replace all instances of experimental_afterEach with afterEach in your stories, preview files, and configuration.
```diff
export const MyStory = {
- experimental_afterEach: async ({ canvasElement }) => {
+ afterEach: async ({ canvasElement }) => {
// cleanup logic
},
};
```
#### Testing Module Changes
The `TESTING_MODULE_RUN_ALL_REQUEST` event has been removed:
```diff
- import { TESTING_MODULE_RUN_ALL_REQUEST } from '@storybook/core-events';
+ import { TESTING_MODULE_RUN_REQUEST } from '@storybook/core-events';
```
#### Consolidate `@storybook/blocks` into addon docs
The package `@storybook/blocks` is no longer published as of Storybook 9.
All exports can now be found in the export `@storybook/addon-docs/blocks`.
Previously, you were able to import all blocks from `@storybook/addon-docs`, this is no longer the case.
This is the only correct import path:
```diff
- import { Meta } from "@storybook/addon-docs";
+ import { Meta } from "@storybook/addon-docs/blocks";
```
### Configuration and Type Changes
#### Manager builder removed alias for `util`, `assert` and `process`
These dependencies (often used accidentally) were polyfilled to mocks or browser equivalents by storybook's manager builder.
Starting with Storybook `9.0`, we no longer alias these anymore.
Adding these aliases meant storybook core, had to depend on these packages, which have a deep dependency graph, added to every storybook project.
If you addon fails to load after this change, we recommend looking at implementing the alias at compile time of your addon, or alternatively look at other bundling config to ensure the correct entries/packages/dependencies are used.
#### Type System Updates
The following types have been removed:
- `Addon_SidebarBottomType`
- `Addon_SidebarTopType`
- `DeprecatedState`
Import paths have been updated:
```diff
- import { SupportedRenderers } from './project_types';
+ import { SupportedRenderers } from 'storybook/internal/types';
```
#### CSF File Changes
Deprecated getters have been removed from the CsfFile class:
- `_fileName`
- `_makeTitle`
#### React-Native config dir renamed
In Storybook 9, React Native (RN) projects use the `.rnstorybook` config directory instead of `.storybook`.
That makes it easier for RN and React Native Web (RNW) storybooks to co-exist in the same project.
To upgrade, either rename your `.storybook` directory to `.rnstorybook` or if you wish to continue using `.storybook` (not recommended), you can use the [`configPath`](https://github.com/storybookjs/react-native#configpath) option to specify `.storybook` manually.
#### `parameters.docs.source.format` removal
The `parameters.docs.source.format` parameter has been removed in favor of using `parameters.docs.source.transform`. If you were using `format` to prettify your code via prettier, you can now use the `transform` parameter with Prettier directly:
```js
// .storybook/preview.js|ts|jsx|tsx
export default {
parameters: {
docs: {
source: {
transform: async (source) => {
const prettier = await import("prettier/standalone");
const prettierPluginBabel = await import("prettier/plugins/babel");
const prettierPluginEstree = await import("prettier/plugins/estree");
return prettier.format(source, {
parser: "babel",
plugins: [prettierPluginBabel, prettierPluginEstree],
});
},
},
},
},
};
```
This change gives you more control over how your code is formatted and allows for asynchronous transformations. The `transform` function receives the source code and story context as parameters, enabling you to implement custom formatting logic or use any code formatting library of your choice.
**Before:**
```js
export const MyStory = {
parameters: {
docs: {
source: {
format: "html",
},
},
},
};
```
**After:**
```js
export const MyStory = {
parameters: {
docs: {
source: {
transform: async (source) => {
// Your custom transformation logic here
return source;
},
},
},
},
};
```
#### `parameter docs.source.excludeDecorators` has no effect in React
#### Documentation Generation Changes
The `autodocs` configuration option has been removed in favor of using tags:
```diff
// .storybook/preview.js
export default {
- docs: { autodocs: true }
};
// In your CSF files:
+ export default {
+ tags: ['autodocs']
+ };
```
In React, the parameter `docs.source.excludeDecorators` option is no longer used.
Decorators are always excluded as it causes performance issues and doc source snippets not showing the actual component.
### Framework-specific changes
#### Svelte: Require v5 and up
Storybook has dropped support for Svelte versions 3 and 4. The minimum supported version is now Svelte 5.
If you're using an older version of Svelte, you'll need to upgrade to Svelte 5 or newer to use the latest version of Storybook.
#### Svelte: Dropped support for @storybook/svelte-webpack5
In Storybook 9.0, we've dropped support for `@storybook/svelte-webpack5`. If you're currently using it, you need to migrate to `@storybook/svelte-vite` instead.
Follow these steps to migrate:
1. Remove the webpack5 framework package:
```bash
npm uninstall @storybook/svelte-webpack5
# or
yarn remove @storybook/svelte-webpack5
```
2. Install the Vite framework package:
```bash
npm install -D @storybook/svelte-vite
# or
yarn add -D @storybook/svelte-vite
```
3. Update your Storybook configuration in `.storybook/main.js` or `.storybook/main.ts`:
```diff
export default {
framework: {
- name: '@storybook/svelte-webpack5'
+ name: '@storybook/svelte-vite',
},
// ...other configuration
};
```
For more details, please refer to the [Svelte & Vite documentation](https://storybook.js.org/docs/get-started/frameworks/svelte-vite).
#### Svelte: Dropped automatic docgen for events and slots
The internal docgen logic for legacy Svelte components have been changed to match what already happened for rune-based components, using the same `svelte2tsx` parsing that the official Svelte tools use.
This means that argTypes are no longer automatically generated for slots and events defined with `on:my-event`.
#### Angular: Require v18 and up
Storybook has dropped support for Angular versions 15-17. The minimum supported version is now Angular 18.
If you're using an older version of Angular, you'll need to upgrade to Angular 18 or newer to use the latest version of Storybook.
Key changes:
- All Angular packages in peerDependencies now require `>=18.0.0 < 20.0.0`
- Removed legacy code supporting Angular < 18
- Standalone components are now the default (can be opted out by explicitly setting `standalone: false` in component decorators)
- Updated RxJS requirement to `^7.4.0`
- Updated TypeScript requirement to `^4.9.0 || ^5.0.0`
- Updated Zone.js requirement to `^0.14.0 || ^0.15.0`
#### Angular: Introduce `features.angularFilterNonInputControls`
Storybook has added a new feature flag `angularFilterNonInputControls` which filters out non-input controls from Angular compoennts in Storybook's controls panel.
To enable it, just set the feature flag in your `.storybook/main.<js|ts> file.
```tsx
export default {
features: {
angularFilterNonInputControls: true,
},
// ... other configurations
};
```
#### Dropped webpack5 Builder Support in Favor of Vite
Removed webpack5 builder support for Preact, Vue3, and Web Components frameworks in favor of Vite builder. This change streamlines our builder support and improves performance across these frameworks.
Removed Packages
- `@storybook/preact-webpack5`
- `@storybook/preset-preact-webpack5`
- `@storybook/vue3-webpack5`
- `@storybook/preset-vue3-webpack`
- `@storybook/web-components-webpack5`
- `@storybook/html-webpack5`
- `@storybook/preset-html-webpack`
**For Preact Projects**
```bash
npm remove @storybook/preact-webpack5 @storybook/preset-preact-webpack
npm install @storybook/preact-vite --save-dev
```
**For Vue3 Projects**
```bash
npm remove @storybook/vue3-webpack5 @storybook/preset-vue3-webpack
npm install @storybook/vue3-vite --save-dev
```
**For Web Components Projects**
```bash
npm remove @storybook/web-components-webpack5
npm install @storybook/web-components-vite --save-dev
```
**For HTML Projects**
```bash
npm remove @storybook/html-webpack5 @storybook/preset-html-webpack
npm install @storybook/html-vite --save-dev
```
**Update .storybook/main.js|ts**
For all affected frameworks, update your configuration to use the Vite builder:
```tsx
export default {
framework: {
name: "@storybook/[framework]-vite", // replace [framework] with preact, vue3, or web-components
options: {},
},
// ... other configurations
};
```
This change consolidates our builder support around Vite, which offers better performance and a more streamlined development experience. The webpack5 builders for these frameworks have been deprecated in favor of the more modern Vite-based solution.
#### Next.js: Require v14 and up
Storybook has dropped support for Next.js versions below 14.1. The minimum supported version is now Next.js 14.1.
If you're using an older version of Next.js, you'll need to upgrade to Next.js 14.1 or newer to use the latest version of Storybook.
For help upgrading your Next.js application, see the [Next.js upgrade guide](https://nextjs.org/docs/app/building-your-application/upgrading).
#### Next.js: Vite builder stabilized
The experimental Next.js Vite builder (`@storybook/experimental-nextjs-vite`) has been stabilized and renamed to `@storybook/nextjs-vite`. If you were using the experimental package, you should update your dependencies to use the new stable package name.
```diff
{
"dependencies": {
- "@storybook/experimental-nextjs-vite": "^x.x.x"
+ "@storybook/nextjs-vite": "^9.0.0"
}
}
```
Also update your `.storybook/main.<js|ts>` file accordingly:
```diff
export default {
addons: [
- "@storybook/experimental-nextjs-vite",
+ "@storybook/nextjs-vite"
]
}
```
#### Lit = Require v3 and up
The minimum supported version is now v3.
## From version 8.5.x to 8.6.x
### Angular: Support experimental zoneless support
Storybook now supports [Angular's experimental zoneless mode](https://angular.dev/guide/experimental/zoneless). This mode is intended to improve performance by removing Angular's zone.js dependency. To enable zoneless mode in your Angular Storybook, set the `experimentalZoneless` config in your `angular.json` file:
```diff
{
"projects": {
"your-project": {
"architect": {
"storybook": {
...
"options": {
...
+ "experimentalZoneless": true
}
}
"build-storybook": {
...
"options": {
...
+ "experimentalZoneless": true
}
}
}
}
}
}
```
### Addon-a11y: Replaced experimental `ally-test` tag behavior with `parameters.a11y.test`
In Storybook 8.6, the `ally-test` tag behavior in the Accessibility addon (`@storybook/addon-a11y`) has been replaced with the `parameters.a11y.test` parameter. See the comparison table below.
| Previous tag value | New parameter value | Description |
| ------------------ | ------------------- | ------------------------------------------------------------------------------------------------------ |
| `'!ally-test'` | `'off'` | Do not run accessibility tests (you can still manually verify via the addon panel) |
| N/A | `'todo'` | Run accessibility tests; violations return a warning in the Storybook UI and a summary count in CLI/CI |
| `'ally-test'` | `'error'` | Run accessibility tests; violations return a failing test in the Storybook UI and CLI/CI |
## From version 8.4.x to 8.5.x
### React Vite: react-docgen-typescript is updated
Storybook now uses [react-docgen-typescript](https://github.com/joshwooding/vite-plugin-react-docgen-typescript) v0.5.0 which updates its internal logic on how it parses files, available under an experimental feature flag `EXPERIMENTAL_useWatchProgram`, which is disabled by default.
Previously, once you made changes to a component's props, the controls and args table would not update unless you restarted Storybook. With the `EXPERIMENTAL_useWatchProgram` flag, you do not need to restart Storybook anymore, however you do need to refresh the browser page. Keep in mind that this flag is experimental and also does not support the `references` field in tsconfig.json files. Depending on how big your codebase is, it might have performance issues.
```ts
// .storybook/main.ts
const config = {
// ...
typescript: {
reactDocgen: "react-docgen-typescript",
reactDocgenTypescriptOptions: {
EXPERIMENTAL_useWatchProgram: true,
},
},
};
export default config;
```
### Introducing features.developmentModeForBuild
As part of our ongoing efforts to improve the testability and debuggability of Storybook, we are introducing a new feature flag: `developmentModeForBuild`. This feature flag allows you to set `process.env.NODE_ENV` to `development` in built Storybooks, enabling development-related optimizations that are typically disabled in production builds.
In development mode, React and other libraries often include additional checks and warnings that help catch potential issues early. These checks are usually stripped out in production builds to optimize performance. However, when running tests or debugging issues in a built Storybook, having these additional checks can be incredibly valuable. One such feature is React's `act`, which ensures that all updates related to a test are processed and applied before making assertions. `act` is crucial for reliable and predictable test results, but it only works correctly when `NODE_ENV` is set to `development`.
```js
// .storybook/main.js
export default {
features: {
developmentModeForBuild: true,
},
};
```
### Added source code panel to docs
Storybook Docs (`@storybook/addon-docs`) now can automatically add a new addon panel to stories that displays a source snippet beneath each story. This is an experimental feature that works similarly to the existing [source snippet doc block](https://storybook.js.org/docs/writing-docs/doc-blocks#source), but in the story view. It is intended to replace the [Storysource addon](https://storybook.js.org/addons/@storybook/addon-storysource).
To enable this globally, add the following line to your project configuration. You can also configure at the component/story level.
```js
// .storybook/preview.js
export default {
parameters: {
docs: {
codePanel: true,
},
},
};
```
### Addon-a11y: Component test integration
In Storybook 8.4, we introduced the [Test addon](https://storybook.js.org/docs/writing-tests/test-addon) (`@storybook/experimental-addon-test`). Powered by Vitest under the hood, this addon lets you watch, run, and debug your component tests directly in Storybook.
In Storybook 8.5, we revamped the [Accessibility addon](https://storybook.js.org/docs/writing-tests/accessibility-testing) (`@storybook/addon-a11y`) to integrate it with the component tests feature. This means you can now extend your component tests to include accessibility tests.
If you upgrade to Storybook 8.5 via `npx storybook@latest upgrade`, the Accessibility addon will be automatically configured to work with the component tests. However, if you're upgrading manually and you have the Test addon installed, adjust your configuration as follows:
```diff
// .storybook/vitest.setup.ts
...
+import * as a11yAddonAnnotations from '@storybook/addon-a11y/preview';
const annotations = setProjectAnnotations([
previewAnnotations,
+ a11yAddonAnnotations,
]);
// Run Storybook's beforeAll hook
beforeAll(annotations.beforeAll);
```
### Addon-a11y: Changing the default element selector
In Storybook 8.5, we changed the default element selector used by the Accessibility addon from `#storybook-root` to `body`. This change was made to align with the default element selector used by the Test addon when running accessibility tests via Vitest. Additionally, Tooltips or Popovers that are rendered outside the `#storybook-root` element will now be included in the accessibility tests per default allowing for a more comprehensive test coverage. If you want to fall back to the previous behavior, you can set the `a11y.element` parameter in your `.storybook/preview.<ts|js>` configuration:
```diff
// .storybook/preview.js
export const parameters = {
a11y: {
+ element: '#storybook-root',
},
};
```
### Addon-a11y: Deprecated `parameters.a11y.manual`
We have deprecated `parameters.a11y.manual` in 8.5. Please use `globals.a11y.manual` instead.
### Addon-test: You should no longer copy the content of `viteFinal` to your configuration
In version 8.4 of `@storybook/experimental-addon-test`, it was required to copy any custom configuration you had in `viteFinal` in `main.ts`, to the Vitest Storybook project. This is no longer necessary, as the Storybook Test plugin will automatically include your `viteFinal` configuration. You should remove any configurations you might already have in `viteFinal` to remove duplicates.
This is especially the case for any plugins you might have, as they could now end up being loaded twice, which is likely to cause errors when running tests. In 8.4 we documented and automatically added some Vite plugins from Storybook frameworks like `@storybook/experimental-nextjs-vite` and `@storybook/sveltekit` - **these needs to be removed as well**.
### Addon-test: Indexing behavior of @storybook/experimental-addon-test is changed
The Storybook test addon used to index stories based on the `test.include` field in the Vitest config file. This caused indexing issues with Storybook, because stories could have been indexed by Storybook and not Vitest, and vice versa. Starting in Storybook 8.5.0-alpha.18, we changed the indexing behavior so that it always uses the globs defined in the `stories` field in `.storybook/main.js` for a more consistent experience. It is now discouraged to use `test.include`, please remove it.
## From version 8.2.x to 8.3.x
### Removed `experimental_SIDEBAR_BOTTOM` and deprecated `experimental_SIDEBAR_TOP` addon types
The experimental SIDEBAR_BOTTOM addon type was removed in favor of a built-in filter UI. The enum type definition will remain available until Storybook 9.0 but will be ignored. Similarly the experimental SIDEBAR_TOP addon type is deprecated and will be removed in a future version.
These APIs allowed addons to render arbitrary content in the Storybook sidebar. Due to potential conflicts between addons and challenges regarding styling, these APIs are/will be removed. In the future, Storybook will provide declarative API hooks to allow addons to add content to the sidebar without risk of conflicts or UI inconsistencies. One such API is `experimental_updateStatus` which allow addons to set a status for stories. The SIDEBAR_BOTTOM slot is now used to allow filtering stories with a given status.
### New parameters format for addon backgrounds
> [!NOTE]
> You need to set the feature flag `backgroundsStoryGlobals` to `true` in your `.storybook/main.ts` to use the new format and set the value with `globals`.
>
> See here how to set feature flags: https://storybook.js.org/docs/api/main-config/main-config-features
> The `addon-backgrounds` addon now uses a new format for configuring its list of selectable backgrounds.
> The `backgrounds` parameter is now an object with an `options` property.
> This `options` object is a key-value pair where the key is used when setting the global value, the value is an object with a `name` and `value` property.
```diff
// .storybook/preview.js
export const parameters = {
backgrounds: {
- values: [
- { name: 'twitter', value: '#00aced' },
- { name: 'facebook', value: '#3b5998' },
- ],
+ options: {
+ twitter: { name: 'Twitter', value: '#00aced' },
+ facebook: { name: 'Facebook', value: '#3b5998' },
+ },
},
};
```
Setting an override value should now be done via a `globals` property on your component/meta or story itself:
```diff
// Button.stories.ts
export default {
component: Button,
- parameters: {
- backgrounds: {
- default: "twitter",
- },
- },
+ globals: {
+ backgrounds: { value: "twitter" },
+ },
};
```
This locks that story to the `twitter` background, it cannot be changed by the addon UI.
### New parameters format for addon viewport
> [!NOTE]
> You need to set the feature flag `viewportStoryGlobals` to `true` in your `.storybook/main.ts` to use the new format and set the value with `globals`.
>
> See here how to set feature flags: https://storybook.js.org/docs/api/main-config/main-config-features
> The `addon-viewport` addon now uses a new format for configuring its list of selectable viewports.
> The `viewport` parameter is now an object with an `options` property.
> This `options` object is a key-value pair where the key is used when setting the global value, the value is an object with a `name` and `styles` property.
> The `styles` property is an object with a `width` and a `height` property.
```diff
// .storybook/preview.js
export const parameters = {
viewport: {
- viewports: {
- iphone5: {
- name: 'phone',
- styles: {
- width: '320px',
- height: '568px',
- },
- },
- },
+ options: {
+ iphone5: {
+ name: 'phone',
+ styles: {
+ width: '320px',
+ height: '568px',
+ },
+ },
+ },
},
};
```
Setting an override value should now be done via a `globals` property on your component/meta or story itself.
Also note the change from `defaultOrientation: "landscape"` to `isRotated: true`.
```diff
// Button.stories.ts
export default {
component: Button,
- parameters: {
- viewport: {
- defaultViewport: "iphone5",
- defaultOrientation: "landscape",
- },
- },
+ globals: {
+ viewport: {
+ value: "iphone5",
+ isRotated: true,
+ },
+ },
};
```
This locks that story to the `iphone5` viewport in landscape orientation, it cannot be changed by the addon UI.
## From version 8.1.x to 8.2.x
### Failed to resolve import "@storybook/X" error
Storybook's package structure changed in 8.2. It is a non-breaking change, but can expose missing project dependencies.
This happens when `@storybook/X` is missing in your `package.json`, but your project references `@storybook/X` in your source code (typically in a story file or in a `.storybook` config file). This is a problem with your project, and if it worked in earlier versions of Storybook, it was purely accidental.
Now in Storybook 8.2, that incorrect project configuration no longer works. The solution is to install `@storybook/X` as a dev dependency and re-run.
Example errors:
```sh
Cannot find module @storybook/preview-api or its corresponding type declarations
```
```sh
Internal server error: Failed to resolve import "@storybook/theming/create" from ".storybook/theme.ts". Does the file exist?
```
To protect your project from missing dependencies, try the `no-extraneous-dependencies` rule in [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import).
### Preview.js globals renamed to initialGlobals
Starting in 8.2 `preview.js` `globals` are deprecated and have been renamed to `initialGlobals`. We will remove `preview.js` `globals` in 9.0.
```diff
// .storybook/preview.js
export default {
- globals: [ a: 1, b: 2 ],
+ initialGlobals: [ a: 1, b: 2 ],
}
```
## From version 8.0.x to 8.1.x
### Portable stories
#### @storybook/nextjs requires specific path aliases to be setup
In order to properly mock the `next/router`, `next/header`, `next/navigation` and `next/cache` APIs, the `@storybook/nextjs` framework includes internal Webpack aliases to those modules. If you use portable stories in your Jest tests, you should set the aliases in your Jest config files `moduleNameMapper` property using the `getPackageAliases` helper from `@storybook/nextjs/export-mocks`:
```js
const nextJest = require("next/jest.js");
const { getPackageAliases } = require("@storybook/nextjs/export-mocks");
const createJestConfig = nextJest();
const customJestConfig = {
moduleNameMapper: {
...getPackageAliases(), // Add aliases for @storybook/nextjs mocks
},
};
module.exports = createJestConfig(customJestConfig);
```
This will make sure you end using the correct implementation of the packages and avoid having issues in your tests.
### main.js `docs.autodocs` is deprecated
The `docs.autodocs` setting in `main.js` is deprecated in 8.1 and will be removed in 9.0.
It has been replaced with a tags-based system which is more flexible than before.
`docs.autodocs` takes three values:
- `true`: generate autodocs for every component
- `false`: don't generate autodocs at all
- `tag`: generate autodocs for components that have been tagged `'autodocs'`.
Starting in 8.1, to generate autodocs for every component (`docs.autodocs = true`), add the following code to `.storybook/preview.js`:
```js
// .storybook/preview.js
export default {
tags: ["autodocs"],
};
```
Tags cascade, so setting `'autodocs'` at the project level automatically propagates to every component and story. If you set autodocs globally and want to opt-out for a particular component, you can remove the `'autodocs'` tag for a component like this:
```js
// Button.stories.ts
export default {
component: Button,
tags: ["!autodocs"],
};
```
If you had set `docs.autodocs = 'tag'`, the default setting, you can remove the setting from `.storybook/main.js`. That is now the default behavior.
If you had set `docs.autodocs = false`, this still works in 8.x, but will go away in 9.0 as a breaking change. If you don't want autodocs at all, simply remove the `'autodocs'` tag throughout your Storybook and autodocs will not be created.
### `docs` and `story` system tags removed
Storybook automatically added the tag `'docs'` to any docs entry in the index and `'story'` to any story entry in the index. This behavior was undocumented, and in an effort to reduce the number of tags we've removed them in 8.1. If you depended on these tags, please file an issue on the [Storybook monorepo](https://github.com/storybookjs/storybook) and let us know!
### Subtitle block and `parameters.componentSubtitle`
The `Subtitle` block now accepts an `of` prop, which can be a reference to a CSF file or a default export (meta).
`parameters.componentSubtitle` has been deprecated to be consistent with other parameters related to autodocs, instead use `parameters.docs.subtitle`.
### Title block `of` prop
The `Title` block now accepts an `of` prop, which can be a reference to a CSF file or a default export (meta).
It still accepts being passed `children`.
Here is the extracted information necessary for an LLM to understand and write about mocking in Storybook.
## Core Concept: Mocking
Components often depend on other modules, such as other components, utility functions, or libraries. These can be from external packages or internal to your project. When rendering those components in Storybook or testing them, you may want to mock those modules to control their behavior and isolate the component's functionality.
For example, this simple component depends on two modules, a local utility function to access the user's browser session and an external package to generate a unique ID:
```
import { v4 as uuidv4 } from 'uuid';
import { getUserFromSession } from '../lib/session';
export function AuthButton() {
const user = getUserFromSession();
const id = uuidv4();
return (
<button onClick={() => { console.log(`User: ${user.name}, ID: ${id}`) }}>
{user ? `Welcome, ${user.name}` : 'Sign in'}
</button>
);
}
```
The above example is written with React, but the same principles apply to other renderers like Vue, Svelte, or Web Components. The important part is the usage of the two module dependencies.
When writing stories or tests for this component, you may want to mock the getUserFromSession function to control the user data returned, or mock the uuidv4 function to return a predictable ID. This allows you to test the component's behavior without relying on the actual implementations of these modules.
For maximum flexibility, Storybook provides three ways to mock modules for your stories. Let's walk through each of them, starting with the most straightforward approach.
### Automocking
Automocking is the most straightforward way to mock modules in Storybook, and we recommend it for all projects using the Vite and Webpack builders (other builders must use one of the other techniques, below). This approach requires minimal configuration while allowing for flexible mocking of modules.
It works with two steps. First, register the modules you want to mock in your Storybook configuration. Then, control the behavior and make assertions about the mocked modules in your stories.
### Registering modules to mock
When automocking, you use the sb.mock utility function to register modules you want to mock. There are three ways to register modules: as spy-only, fully automocked, or with a mock file. Each method has its use cases and benefits.
There are some key details to keep in mind when using the sb.mock utility:
You can register both local modules (e.g., ../lib/session.ts) and packages in node_modules (e.g., uuid).
You can only register mocked modules in your project-level configuration: .storybook/preview.js|ts. This ensures consistent and performant mocking across all stories in your project. You can modify the behavior of these modules in your stories, but you cannot register them directly in the story files.
When registering a mock for a local module, the path must:
Not use an alias or subpath import (e.g., @/lib/session.ts or #lib/session).
Be relative to the .storybook/preview.js|ts file.
Include the file extension (e.g., .ts or .js).
If you are using Typescript, you can wrap the module path in import() to ensure the module is correctly resolved and typed. For example, sb.mock(import('../lib/session.ts')).
If you are using the Webpack builder, you can only automock node_module packages that have ESModules (ESM) entry points. If a module has both CommonJS (CJS) and ESM entry points, Webpack doesn't correctly resolve the ESM entry and it cannot be mocked. Webpack users can still mock CJS node_module packages by providing a mock file.
### Spy-only
For most cases, you should register a mocked module as spy-only, by setting the spy option to true. This leaves the original module's functionality intact, while still allowing you to modify the behavior if needed and make assertions in your tests.
For example, if you want to spy on the getUserFromSession function and the uuidv4 function from the uuid package, you can call the sb.mock utility function in your .storybook/preview.js|ts file:
```
import { sb } from 'storybook/test';
// πŸ‘‡ Automatically spies on all exports from the `lib/session` local module
sb.mock(import('../lib/session.ts'), { spy: true });
// πŸ‘‡ Automatically spies on all exports from the `uuid` package in `node_modules`
sb.mock(import('uuid'), { spy: true });
// ...rest of the file
```
If you need to mock an external module that has a deeper import path (e.g. lodash-es/add), register the mock with that path.
You can then control the behavior of these modules and make assertions about them in your stories, such as checking if a function was called or what arguments it was called with.
### Fully automocked modules
For cases where you need to prevent the original module's functionality from executing, set the spy option to false (or omit it, because that is the default value). This will automatically replace all exports from the module with Vitest mock functions, allowing you to control their behavior and make assertions while being certain that the original functionality never runs.
```
import { sb } from 'storybook/test';
// πŸ‘‡ Automatically replaces all exports from the `lib/session` local module with mock functions
sb.mock(import('../lib/session.ts'));
// πŸ‘‡ Automatically replaces all exports from the `uuid` package in `node_modules` with mock functions
sb.mock(import('uuid'));
// ...rest of the file
```
### Mock files
If you want to mock a module with more complex behavior or reuse a mock's behavior across multiple stories, you can create a mock file. This file should be placed in a __mocks__ directory next to the module you want to mock, and it should export the same named exports as the original module.
For example, to mock the session module in the lib directory, create a file named session.js|ts in the lib/__mocks__ directory:
```
// lib/__mocks__/session.js
export function getUserFromSession() {
return { name: 'Mocked User' };
}
```
For packages in your node_modules, create a __mocks__ directory in the root of your project and create the mock file there. For example, to mock the uuid package, create a file named uuid.js in the __mocks__ directory:
```
// __mocks__/uuid.js
export function v4() {
return '1234-5678-90ab-cdef';
}
```
The root of your project is determined differently depending on your builder:
Vite projects
The root __mocks__ directory should be placed in the root directory, as defined in your project's Vite configuration (typically process.cwd()) If that is unavailable, it defaults to the directory containing your .storybook directory.
Webpack projects
The root __mocks__ directory should be placed in the context directory, as defined in your project's Webpack configuration (typically process.cwd()). If that is unavailable, it defaults to the root of your repository.
You can then use the sb.mock utility to register these mock files in your preview.js|ts file:
```
//.storybook/preview.<js|ts>
import { sb } from 'storybook/test';
// πŸ‘‡ Replaces imports of this module with imports to `../lib/__mocks__/session.ts`
sb.mock(import('../lib/session.ts'));
// πŸ‘‡ Replaces imports of this module with imports to `../__mocks__/uuid.ts`
sb.mock(import('uuid'));
// ...rest of the file
```
Note that the API for registering automatically mocked modules and mock files is the same. The only difference is that sb.mock will first look for a mock file in the appropriate directory before automatically mocking the module.
### Using automocked modules in stories
All registered automocked modules are used the same way within your stories. You can control the behavior, such as defining what it returns, and make assertions about the modules.
```
// AuthButton.stories.ts
// Replace your-framework with the name of your framework (e.g. react-vite, vue3-vite, etc.)
import type { Meta, StoryObj } from '@storybook/your-framework';
import { expect, mocked } from 'storybook/test';
import { AuthButton } from './AuthButton';
import { v4 as uuidv4 } from 'uuid';
import { getUserFromSession } from '../lib/session';
Mocked functions created with the sb.mock utility are full Vitest mock functions, which means you can use all the methods available on them. Some of the most useful methods include:
Method Description
mockReturnValue(value) Sets the return value of the mocked function.
mockResolvedValue(value) Sets the value the mocked async function resolves to.
mockImplementation(fn) Sets a custom implementation for the mocked function.
### Spying on mocked modules
The fn utility also spies on the original module's functions, which you can use to assert their behavior in your tests. For example, you can use interaction tests to verify that a function was called with specific arguments.
For example, this story checks that the saveNote function was called when the user clicks the save button:
NoteUI.stories.ts
// Replace your-framework with svelte-vite or sveltekit
import type { Meta, StoryObj } from '@storybook/your-framework';
import { expect } from 'storybook/test';
// πŸ‘‡ Automocked module resolves to '../app/__mocks__/actions'
import { saveNote } from '../app/actions';
import { createNotes } from '../app/mocks/notes';
import NoteUI from './note-ui.svelte';
const meta = {
title: 'Mocked/NoteUI',
component: NoteUI,
} satisfies Meta<typeof NoteUI>;
export default meta;
type Story = StoryObj<typeof meta>;
const notes = createNotes();
export const SaveFlow: Story = {
name: 'Save Flow β–Ά',
args: {
isEditing: true,
note: notes[0],
},
play: async ({ canvas, userEvent }) => {
const saveButton = canvas.getByRole('menuitem', { name: /done/i });
await userEvent.click(saveButton);
// πŸ‘‡ This is the mock function, so you can assert its behavior
await expect(saveNote).toHaveBeenCalled();
},
};
const meta = {
component: AuthButton,
// πŸ‘‡ This will run before each story is rendered
beforeEach: async () => {
// πŸ‘‡ Force known, consistent behavior for mocked modules
mocked(uuidv4).mockReturnValue('1234-5678-90ab-cdef');
mocked(getUserFromSession).mockReturnValue({ name: 'John Doe' });
},
} satisfies Meta<typeof AuthButton>;
export default meta;
type Story = StoryObj<typeof meta>;
export const LogIn: Story = {
play: async ({ canvas, userEvent }) => {
const button = canvas.getByRole('button', { name: 'Sign in' });
userEvent.click(button);
// Assert that the getUserFromSession function was called
expect(getUserFromSession).toHaveBeenCalled();
},
};
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment