# Storybook
> ReactVueAngularWeb ComponentsMore
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/addon-knowledge-base
# Page: /docs/addons/addon-knowledge-base
# Addon knowledge base
ReactVueAngularWeb ComponentsMore
Once you understand the basics of writing addons, there are a variety of common enhancements to make your addon better. This page details additional information about addon creation. Use it as a quick reference guide when creating your own addons.
##
Disable the addon panel
It’s possible to disable the addon panel for a particular story.
To make that possible, you need to pass the `paramKey` element when you register the panel:
/my-addon/manager.js
addons.register(ADDON_ID, () => {
addons.add(PANEL_ID, {
type: types.PANEL,
title: 'My Addon',
render: () =>
Addon tab content
,
paramKey: 'myAddon', // this element
});
});
Then when adding a story, you can pass a disabled parameter.
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/configure/#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Button',
component: Button,
parameters: {
myAddon: { disable: true }, // Disables the addon
},
} satisfies Meta;
export default meta;
##
Style your addon
Storybook uses [Emotion](https://emotion.sh/docs/introduction) for styling. Alongside with a theme that you can customize!
We recommend using Emotion to style your addon’s UI components. That allows you to use the active Storybook theme to deliver a seamless developer experience. If you don’t want to use Emotion, you can use inline styles or another css-in-js lib. You can receive the theme as a prop by using Emotion's `withTheme` HOC. [Read more about theming](../configure/user-interface/theming).
##
Storybook components
Addon authors can develop their UIs using any React library. But we recommend using Storybook’s UI components in `storybook/internal/components` to build addons faster. When you use Storybook components, you get:
* Battle-tested off-the-shelf components
* Storybook native look and feel
* Built-in support for Storybook theming
Use the components listed below with your next addon.
Component| Source| Story
---|---|---
Action Bar| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/ActionBar/ActionBar.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-actionbar--single-item)
Addon Panel| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/addon-panel/addon-panel.tsx)| N/A
Badge| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/Badge/Badge.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-badge--all-badges)
Button| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/Button/Button.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-button--all-buttons)
Form| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/form/index.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-form-button--sizes)
Loader| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/Loader/Loader.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-loader--progress-bar)
PlaceHolder| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/placeholder/placeholder.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-placeholder--single-child)
Scroll Area| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/ScrollArea/ScrollArea.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-scrollarea--vertical)
Space| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/spaced/Spaced.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-spaced--row)
Syntax Highlighter| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/syntaxhighlighter/syntaxhighlighter.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-syntaxhighlighter--bash)
Tabs| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/tabs/tabs.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-tabs--stateful-static)
ToolBar| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/bar/bar.tsx)| N/A
ToolTip| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/tooltip/Tooltip.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-tooltip-tooltip--basic-default)
Zoom| [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/Zoom/Zoom.tsx)| [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-zoom--element-actual-size)
Complementing the components, also included is a set of UI primitives. Use the content listed below as a reference for styling your addon.
Component| Source| Story
---|---|---
Color Palette (see note below)| [See implementation](https://github.com/storybookjs/storybook/blob/next/code/addons/docs/src/blocks/components/ColorPalette.tsx)| [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-colorpalette--page)
Icon| [See implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/icon/icon.tsx)| [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-icon--labels)
Typography| [See implementation](https://github.com/storybookjs/storybook/tree/next/code/core/src/components/components/typography)| [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-typography--all)
ℹ️
The color palette implemented by `@storybook/addon-docs/blocks` is a high-level abstraction of the [`storybook/theming`](https://github.com/storybookjs/storybook/tree/next/code/core/src/theming) module.
##
Build system
When you're developing your addon as a package, you can’t use `npm link` to add it to your project. List your addon as a local dependency into your package.json:
package.json
{
"dependencies": {
"@storybook/addon-controls": "file:///home/username/myrepo"
}
}
ℹ️
Run either `yarn` or `npm install` to install the addon.
##
Hot module replacement
While developing your addon, you can configure HMR (hot module replacement) to reflect the changes made.
##
Standalone Storybook addons
If you're developing a standalone addon, add a new script to `package.json` with the following:
package.json
{
"scripts": {
"start": "npm run build -- --watch"
}
}
###
Local Storybook addons
If you're developing a local Storybook addon built on top of an existing Storybook installation, HMR (hot module replacement) is available out of the box.
##
Composing addons in presets
If you're working on a preset that loads third-party addons, which you don't have control over, and you need access to certain features (e.g., decorators) or provide additional configurations. In that case, you'll need to update your preset to the following to allow you to load and configure the other addons:
my-preset/index.js
function managerEntries(entry = []) {
return [...entry, import.meta.resolve('my-other-addon/manager')];
}
const previewAnnotations = (entry = [], options) => {
return [...entry, import.meta.resolve('my-other-addon/preview')];
};
export default {
managerEntries,
previewAnnotations,
};
If you have control over the addons you want to customize. In that case, you can update your preset and implement a custom function to load any additional presets and provide the necessary configuration.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/addon-knowledge-base.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/addon-migration-guide
# Page: /docs/addons/addon-migration-guide
# Addon migration guide for Storybook 10.0
ReactVueAngularWeb ComponentsMore
We sincerely appreciate the dedication and effort addon creators put into keeping the Storybook ecosystem vibrant and up-to-date. As Storybook evolves to version 10.0, bringing new features and improvements, this guide is here to assist you in migrating your addons from 9.x to 10.0. If you need to migrate your addon from an earlier version of Storybook, please first refer to the [Addon migration guide for Storybook 9.0](../../../docs/9/addons/addon-migration-guide).
ℹ️
We also have a general [Storybook migration guide](../releases/migration-guide) that covers updates to your Storybook instance rather than your addon code.
##
Dependency updates
You will need to update your Storybook dependencies. Peer dependencies must point to `^10.0.0` to ensure broad compatibility for your end users. Development dependencies can be set to `^10.0.0`, or to `next` if you want to try the latest prerelease all year round.
package.json
{
"devDependencies": {
"@storybook/addon-docs": "next",
"@storybook/react-vite": "next",
"storybook": "next"
},
"peerDependencies": {
"storybook": "^10.0.0"
}
}
If you still have `@storybook/addon-essentials`, `@storybook/addon-interactions`, `@storybook/addon-links`, or `@storybook/blocks` in your dependencies, you will need to remove them. These packages are empty since Storybook 9 and will no longer be published going forward.
###
Supporting earlier versions
If your addon supports multiple major versions of Storybook, you can specify a wider version range in your peer dependencies:
package.json
{
"name": "your-storybook-addon",
"peerDependencies": {
"storybook": "^9.0.0 || ^10.0.0"
},
"devDependencies": {
"storybook": ">=10.0.0-0 <11.0.0-0" // For local development
}
}
However, we recommend releasing a new major version of your addon alongside new major versions of Storybook. This practice:
1. Makes it easier to maintain your code
2. Allows you to take advantage of new features and improvements
3. Provides a clearer upgrade path for your users
##
Key changes for addons
Here are the changes in version 10.0 that impact addon development.
###
ESM-only builds
Storybook 10 requires all addons to be built as ESM-only. This change simplifies the build process and reduces maintenance overhead. You'll need to make many changes to `tsup.config.ts`, so it can be easier to copy the reference file in the [`addon-kit` repository](https://github.com/storybookjs/addon-kit/blob/main/tsup.config.ts).
This update brings the following changes:
* The Node target moves from Node 20.0 to Node 20.19
* You no longer need to build CJS files
* You no longer need to pass `globalManagerPackages` and `globalPreviewPackages`
* The `bundler` config in `package.json` no longer needs to be manually typed
* We recommend you stop using `exportEntries` and switch exported entries to `previewEntries` and `managerEntries` instead based on where they are consumed by your users
tsup.config.ts
- import { readFile } from "node:fs/promises";
import { defineConfig, type Options } from "tsup";
- import { globalPackages as globalManagerPackages } from "storybook/internal/manager/globals";
- import { globalPackages as globalPreviewPackages } from "storybook/internal/preview/globals";
- const NODE_TARGET: Options["target"] = "node20";
+ const NODE_TARGET = "node20.19"; // Minimum Node version supported by Storybook 10
- type BundlerConfig = {
- bundler?: {
- exportEntries?: string[];
- nodeEntries?: string[];
- managerEntries?: string[];
- previewEntries?: string[];
- };
- };
export default defineConfig(async (options) => {
// reading the three types of entries from package.json, which has the following structure:
// {
// ...
// "bundler": {
- // "exportEntries": ["./src/index.ts"],
// "managerEntries": ["./src/manager.ts"],
- // "previewEntries": ["./src/preview.ts"],
+ // "previewEntries": ["./src/preview.ts", "./src/index.ts"],
// "nodeEntries": ["./src/preset.ts"]
// }
// }
- const packageJson = (await readFile("./package.json", "utf8").then(
- JSON.parse,
- )) as BundlerConfig;
+ const packageJson = (
+ await import("./package.json", { with: { type: "json" } })
+ ).default;
+
const {
bundler: {
- exportEntries = [],
managerEntries = [],
previewEntries = [],
nodeEntries = [],
- } = {},
+ },
} = packageJson;
const commonConfig: Options = {
- splitting: false,
+ splitting: true,
+ format: ["esm"],
- minify: !options.watch,
treeshake: true,
- sourcemap: true,
// keep this line commented until https://github.com/egoist/tsup/issues/1270 is resolved
// clean: options.watch ? false : true,
clean: false,
+ // The following packages are provided by Storybook and should always be externalized
+ // Meaning they shouldn't be bundled with the addon, and they shouldn't be regular dependencies either
+ external: ["react", "react-dom", "@storybook/icons"],
};
const configs: Options[] = [];
-
- // export entries are entries meant to be manually imported by the user
- // they are not meant to be loaded by the manager or preview
- // they'll be usable in both node and browser environments, depending on which features and modules they depend on
- if (exportEntries.length) {
- configs.push({
- ...commonConfig,
- entry: exportEntries,
- dts: {
- resolve: true,
- },
- format: ["esm", "cjs"],
- platform: "neutral",
- target: NODE_TARGET,
- external: [...globalManagerPackages, ...globalPreviewPackages],
- });
- }
// manager entries are entries meant to be loaded into the manager UI
// they'll have manager-specific packages externalized and they won't be usable in node
// they won't have types generated for them as they're usually loaded automatically by Storybook
if (managerEntries.length) {
configs.push({
...commonConfig,
entry: managerEntries,
- format: ["esm"],
platform: "browser",
- target: BROWSER_TARGETS,
+ target: "esnext", // we can use esnext for manager entries since Storybook will bundle the addon's manager entries again anyway
- external: globalManagerPackages,
});
}
// preview entries are entries meant to be loaded into the preview iframe
// they'll have preview-specific packages externalized and they won't be usable in node
// they'll have types generated for them so they can be imported when setting up Portable Stories
if (previewEntries.length) {
configs.push({
...commonConfig,
entry: previewEntries,
- dts: {
- resolve: true,
- },
- format: ["esm", "cjs"],
platform: "browser",
- target: BROWSER_TARGETS,
+ target: "esnext", // we can use esnext for preview entries since Storybook will bundle the addon's preview entries again anyway
- external: globalPreviewPackages,
+ dts: true,
});
}
// node entries are entries meant to be used in node-only
// this is useful for presets, which are loaded by Storybook when setting up configurations
// they won't have types generated for them as they're usually loaded automatically by Storybook
if (nodeEntries.length) {
configs.push({
...commonConfig,
entry: nodeEntries,
- format: ["cjs"],
platform: "node",
target: NODE_TARGET,
});
Next, update the `exports` field in your `package.json` to remove mentions of CJS files.
package.json
"exports": {
".": {
"types": "./dist/index.d.ts",
- "import": "./dist/index.js",
- "require": "./dist/index.cjs"
+ "default": "./dist/index.js"
},
"./preview": {
- "types": "./dist/index.d.ts",
- "import": "./dist/preview.js",
- "require": "./dist/preview.cjs"
+ "types": "./dist/preview.d.ts",
+ "default": "./dist/preview.js"
},
- "./preset": "./dist/preset.cjs",
+ "./preset": "./dist/preset.js",
"./manager": "./dist/manager.js",
"./package.json": "./package.json"
},
Update `tsconfig.json`.
tsconfig.json
{
"compilerOptions": {
// …
+ "moduleResolution": "bundler",
// …
- "module": "commonjs",
+ "module": "preserve",
// …
- "target": "ES2020",
+ "target": "esnext",
// …
- "lib": ["es2020", "dom", "dom.iterable"],
+ "lib": ["esnext", "dom", "dom.iterable"],
// …
- "rootDir": "./src",
+ "rootDir": ".",
},
- "include": ["src/**/*"]
+ "include": ["src/**/*", "tsup.config.ts"]
}
Finally, change the `preset.js` file at the top-level of your addon to be ESM. This file used to be CJS because the Storybook Node app only supported CJS.
preset.js
-// this file is slightly misleading. It needs to be CJS, and thus in this "type": "module" package it should be named preset.cjs
-// but Storybook won't pick that filename up so we have to name it preset.js instead
-
-module.exports = require('./dist/preset.cjs');
+export * from './dist/preset.js';
###
Local addon loading
Because addons are now ESM-only, you must change how you load your own addon in your development Storybook instance. Imports and exports must now follow ESM syntax, and relative paths must use `import.meta.resolve`.
Remove `.storybook/local-preset.cjs` and create `.storybook/local-preset.ts` with the following content:
.storybook/local-preset.ts
import { fileURLToPath } from "node:url";
export function previewAnnotations(entry = []) {
return [...entry, fileURLToPath(import.meta.resolve("../dist/preview.js"))];
}
export function managerEntries(entry = []) {
return [...entry, fileURLToPath(import.meta.resolve("../dist/manager.js"))];
}
export * from "../dist/preset.js";
Next, update your `main.ts` to reference the new preset file:
.storybook/main.ts
- addons: ["@storybook/addon-docs", "./local-preset.cjs"],
+ addons: ["@storybook/addon-docs", import.meta.resolve("./local-preset.ts")],
##
Optional changes
The following changes are not strictly required yet, but we recommend making them to improve your users' experience.
###
CSF Factories support
To support CSF Factories annotations in your addon, you will need to update your `src/index.ts` file to use the new `definePreviewAddon`. This feature will be part of [CSF Next](../api/csf/csf-next). This change is highly recommended, as it will help your own users reap the benefits of CSF Factories.
With CSF Factories, users can chain their preview configuration and benefit from better typing and more flexibility. Addons must export annotations to be compatible with this new syntax. CSF Factories will be the default way to write stories in Storybook 11.
src/index.ts
- // make it work with --isolatedModules
- export default {};
+ import { definePreviewAddon } from "storybook/internal/csf";
+ import addonAnnotations from "./preview";
+
+ export default () => definePreviewAddon(addonAnnotations);
###
Removal of exportEntries
The `exportEntries` property in `package.json`'s `bundler` was used to produce the `index.js` build output from `src/index.ts`. It was compatible with Node.js, rather than strictly with browsers. This build configuration could cause subtle bugs when addon authors exported code in `index.js` for use in the Storybook preview or manager.
In the Storybook 10 [addon-kit](https://github.com/storybookjs/addon-kit), we removed `exportEntries` from the `bundler` config, and we moved `src/index.ts` to be part of `previewEntries` instead. This way, any code exported from `src/index.ts` is bundled for browsers and usable with CSF Next. If you need to export additional code to run in the preview (such as optional decorators), you can add them to `src/index.ts`.
If you need to export code for the manager (such as a `renderLabel` function for the sidebar), you can create a new `src/manager-helpers.ts` file and add it to `managerEntries`, like so:
package.json
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./preview": {
"types": "./dist/preview.d.ts",
"default": "./dist/preview.js"
},
"./preset": "./dist/preset.js",
"./manager": "./dist/manager.js",
+ "./manager-helpers": "./dist/manager-helpers.js",
"./package.json": "./package.json"
},
"bundler": {
"managerEntries": [
+ "src/manager-helpers.ts",
"src/manager.tsx"
]
}
###
Build file cleanup
We recommend removing your old build files as you build. This will avoid your `dist` folder growing with expired JS chunks. You may add the following to your `package.json` scripts:
package.json
{
"scripts": {
+ "prebuild": "node -e \"fs.rmSync('./dist', { recursive: true, force: true })\"",
}
}
###
Package keyword changes
We've updated default keywords for addons in the Storybook `addon-kit` template.
package.json
"keywords": [
- "storybook-addons",
+ "storybook-addon",
],
##
10.0.0 full migration guide
For a full list of changes, please visit the [Migration.md](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-9x-to-1000) file
##
Migration example
For a complete example of an addon updated to support Storybook 10.0, refer to the [Addon Kit migration PR](https://github.com/storybookjs/addon-kit/pull/82). Once merged, it will demonstrate all the necessary and recommended changes for Storybook 10.
##
Releasing
To support Storybook 10.0, we encourage you to release a new major version of your addon. For experimental features or testing, use the `next` tag. This allows you to gather feedback before releasing a stable version.
##
Support
If you're having issues with your addon after following this guide, please open a [new discussion](https://github.com/storybookjs/storybook/discussions/new?category=migrations) in our GitHub repository or come talk to us in our [dedicated addon developer channel, `#addons`](https://discord.gg/KKXFQy9sFc) on Discord.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/addon-migration-guide.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/addon-types
# Page: /docs/addons/addon-types
# Types of addons
ReactVueAngularWeb ComponentsMore
Each Storybook addon is classified into two general categories, UI-based or Presets. Each type of addons feature is documented here. Use this as a reference when creating your addon.
##
UI-based addons
UI-based addons allow you to customize Storybook's UI with the following elements.
###
Panels
Panel addons allow you to add your own UI in Storybook's addon panel. This is the most common type of addon in the ecosystem. For example, the official [`@storybook/addon-a11y`](https://github.com/storybookjs/storybook/tree/next/code/addons/a11y) uses this pattern.

Use this boilerplate code to add a new `Panel` to Storybook's UI:
addon-panel/manager.js
import React from 'react';
import { AddonPanel } from 'storybook/internal/components';
import { useGlobals, addons, types } from 'storybook/manager-api';
addons.register('my/panel', () => {
addons.add('my-panel-addon/panel', {
title: 'Example Storybook panel',
//👇 Sets the type of UI element in Storybook
type: types.PANEL,
render: ({ active }) => (
I'm a panel addon in Storybook
),
});
});
###
Toolbars
Toolbar addons allow you to add your own custom tools in Storybook's Toolbar. For example, the official [`@storybook/addon-themes`](https://storybook.js.org/addons/@storybook/addon-themes) uses this pattern.

Use this boilerplate code to add a new `button` to Storybook's Toolbar:
addon-toolbar/manager.js
import React from 'react';
import { addons, types } from 'storybook/manager-api';
import { ToggleButton } from 'storybook/internal/components';
import { OutlineIcon } from '@storybook/icons';
addons.register('my-addon', () => {
addons.add('my-addon/toolbar', {
title: 'Example Storybook toolbar',
//👇 Sets the type of UI element in Storybook
type: types.TOOL,
//👇 Shows the Toolbar UI element if the story canvas is being viewed
match: ({ tabId, viewMode }) => !tabId && viewMode === 'story',
render: ({ active }) => (
),
});
});
ℹ️
The `match` property allows you to conditionally render your toolbar addon, [based on the current view](./writing-addons#conditionally-render-the-addon). The `icon` element used in the example loads the icons from the `storybook/internal/components` module. See [here](../faq#what-icons-are-available-for-my-toolbar-or-my-addon) for the list of available icons that you can use.
###
Tabs
Tab addons allow you to create your own custom tabs in Storybook.

Use this boilerplate code to add a new `Tab` to Storybook's UI:
addon-tab/manager.js
import React from 'react';
import { addons, types } from 'storybook/manager-api';
addons.register('my-addon', () => {
addons.add('my-addon/tab', {
type: types.TAB,
title: 'Example Storybook tab',
render: () => (
I'm a tabbed addon in Storybook
),
});
});
ℹ️
Learn how to write your own addon that includes these UI elements [here](./writing-addons).
##
Preset addons
Storybook preset addons are grouped collections of `babel`, `webpack`, and `addons` configurations to integrate Storybook and other technologies. For example the official [preset-create-react-app](https://github.com/storybookjs/presets/tree/master/packages/preset-create-react-app).
Use this boilerplate code while writing your own preset addon.
.storybook/my-preset.js
export default {
managerWebpack: async (config, options) => {
// Update config here
return config;
},
webpackFinal: async (config, options) => {
return config;
},
babel: async (config, options) => {
return config;
},
};
**Learn more about the Storybook addon ecosystem**
* Types of addons for other types of addons
* [Writing addons](./writing-addons) for the basics of addon development
* [Presets](./writing-presets) for preset development
* [Integration catalog](./integration-catalog) for requirements and available recipes
* [API reference](./addons-api) to learn about the available APIs
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/addon-types.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/addons-api
# Page: /docs/addons/addons-api
# Addon API
ReactVueAngularWeb ComponentsMore
Storybook's API allows developers to interact programmatically with Storybook. With the API, developers can build and deploy custom addons and other tools that enhance Storybook's functionality.
##
Core Addon API
Our API is exposed via two distinct packages, each one with a different purpose:
* `storybook/manager-api` used to interact with the Storybook manager UI or access the Storybook API.
* `storybook/preview-api` used to control and configure the addon's behavior.
my-addon/src/manager.js|ts
import { addons } from 'storybook/preview-api';
import { useStorybookApi } from 'storybook/manager-api';
###
addons.add()
The `add` method allows you to register the type of UI component associated with the addon (e.g., panels, toolbars, tabs). For a minimum viable Storybook addon, you should provide the following arguments:
* `type`: The type of UI component to register.
* `title`: The title to feature in the Addon Panel.
* `render`: The function that renders the addon's UI component.
my-addon/src/manager.js|ts
import React from 'react';
import { addons, types } from 'storybook/manager-api';
import { AddonPanel } from 'storybook/internal/components';
const ADDON_ID = 'myaddon';
const PANEL_ID = `${ADDON_ID}/panel`;
addons.register(ADDON_ID, (api) => {
addons.add(PANEL_ID, {
type: types.PANEL,
title: 'My Addon',
render: ({ active }) => (
Storybook addon panel
),
});
});
ℹ️
The render function is called with `active`. The `active` value will be true when the panel is focused on the UI.
###
addons.register()
Serves as the entry point for all addons. It allows you to register an addon and access the Storybook API. For example:
my-addon/src/manager.js|ts
import { addons } from 'storybook/preview-api';
// Register the addon with a unique name.
addons.register('my-organisation/my-addon', (api) => {});
Now you'll get an instance to our StorybookAPI. See the api docs for Storybook API regarding using that.
###
addons.getChannel()
Get an instance to the channel to communicate with the manager and the preview. You can find this in both the addon register code and your addon’s wrapper component (where used inside a story).
It has a NodeJS [EventEmitter](https://nodejs.org/api/events.html) compatible API. So, you can use it to emit events and listen to events.
my-addon/src/manager.js|ts
import React, { useCallback } from 'react';
import { OutlineIcon } from '@storybook/icons';
import { useGlobals } from 'storybook/manager-api';
import { addons } from 'storybook/preview-api';
import { ToggleButton } from 'storybook/internal/components';
import { FORCE_RE_RENDER } from 'storybook/internal/core-events';
const ExampleToolbar = () => {
const [globals, updateGlobals] = useGlobals();
const isActive = globals['my-param-key'] || false;
// Function that will update the global value and trigger a UI refresh.
const refreshAndUpdateGlobal = () => {
updateGlobals({
['my-param-key']: !isActive,
}),
// Invokes Storybook's addon API method (with the FORCE_RE_RENDER) event to trigger a UI refresh
addons.getChannel().emit(FORCE_RE_RENDER);
};
const toggleToolbarAddon = useCallback(() => refreshAndUpdateGlobal(), [isActive]);
return (
);
};
###
makeDecorator
Use the `makeDecorator` API to create decorators in the style of the official addons. Like so:
my-addon/src/decorator.js|ts
import { makeDecorator } from 'storybook/preview-api';
export const withAddonDecorator = makeDecorator({
name: 'withSomething',
parameterName: 'CustomParameter',
skipIfNoParametersOrOptions: true,
wrapper: (getStory, context, { parameters }) => {
/*
* Write your custom logic here based on the parameters passed in Storybook's stories.
* Although not advised, you can also alter the story output based on the parameters.
*/
return getStory(context);
},
});
ℹ️
If the story's parameters include `{ exampleParameter: { disable: true } }` (where `exampleParameter` is the `parameterName` of your addon), your decorator will not be called.
The `makeDecorator` API requires the following arguments:
* `name`: Unique name to identify the custom addon decorator.
* `parameterName`: Sets a unique parameter to be consumed by the addon.
* `skipIfNoParametersOrOptions`: (Optional) Doesn't run the decorator if the user hasn't options either via [decorators](../writing-stories/decorators) or [parameters](../writing-stories/parameters).
* `wrapper`: your decorator function. Takes the `getStory`, `context`, and both the `options` and `parameters` (as defined in `skipIfNoParametersOrOptions` above).
* * *
##
Storybook API
Storybook's API allows you to access different functionalities of Storybook UI.
###
api.selectStory()
The `selectStory` API method allows you to select a single story. It accepts the following two parameters; story kind name and an optional story name. For example:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
import * as React from 'react';
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/configure/#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Button',
component: Button,
//👇 Creates specific parameters for the story
parameters: {
myAddon: {
data: 'This data is passed to the addon',
},
},
} satisfies Meta;
export default meta;
type Story = StoryObj;
/*
*👇 Render functions are a framework specific feature to allow you control on how the component renders.
* See https://storybook.js.org/docs/api/csf
* to learn how to use render functions.
*/
export const Basic: Story = {
render: () => ,
};
This is how you can select the above story:
my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
api.selectStory('Button', 'Default');
});
###
api.selectInCurrentKind()
Similar to the `selectStory` API method, but it only accepts the story as the only parameter.
my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
api.selectInCurrentKind('Default');
});
###
api.setQueryParams()
This method allows you to set query string parameters. You can use that as temporary storage for addons. Here's how you define query params:
my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
api.setQueryParams({
exampleParameter: 'Sets the example parameter value',
anotherParameter: 'Sets the another parameter value',
});
});
Additionally, if you need to remove a query parameter, set it as `null` instead of removing them from the addon. For example:
my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
api.setQueryParams({
exampleParameter: null,
});
});
###
api.getQueryParam()
Allows retrieval of a query parameter enabled via the `setQueryParams` API method. For example:
my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
api.getQueryParam('exampleParameter');
});
###
api.getUrlState(overrideParams)
This method allows you to get the application URL state, including any overridden or custom parameter values. For example:
my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
const href = api.getUrlState({
selectedKind: 'kind',
selectedStory: 'story',
}).url;
});
###
api.on(eventName, fn)
This method allows you to register a handler function called whenever the user navigates between stories.
my-addon/src/manager.js|ts
addons.register('my-organisation/my-addon', (api) => {
// Logs the event data to the browser console whenever the event is emitted.
api.on('custom-addon-event', (eventData) => console.log(eventData));
});
###
addons.setConfig(config)
This method allows you to override the default Storybook UI configuration (e.g., set up a [theme](../configure/user-interface/theming) or hide UI elements):
.storybook/manager.ts
Typescript
import { addons, type State } from 'storybook/manager-api';
addons.setConfig({
navSize: 300,
bottomPanelHeight: 300,
rightPanelWidth: 300,
panelPosition: 'bottom',
enableShortcuts: true,
showToolbar: true,
theme: undefined,
selectedPanel: undefined,
initialActive: 'sidebar',
layoutCustomisations: {
showSidebar(state: State, defaultValue: boolean) {
return state.storyId === 'landing' ? false : defaultValue;
},
showToolbar(state: State, defaultValue: boolean) {
return state.viewMode === 'docs' ? false : defaultValue;
},
},
sidebar: {
showRoots: false,
collapsedRoots: ['other'],
},
toolbar: {
title: { hidden: false },
zoom: { hidden: false },
eject: { hidden: false },
copy: { hidden: false },
fullscreen: { hidden: false },
},
});
The following table details how to use the API values:
Name| Type| Description| Example Value
---|---|---|---
**navSize**| Number (pixels)| The size of the sidebar that shows a list of stories| `300`
**bottomPanelHeight**| Number (pixels)| The size of the addon panel when in the bottom position| `200`
**rightPanelWidth**| Number (pixels)| The size of the addon panel when in the right position| `200`
**panelPosition**| String| Where to show the addon panel| `'bottom'` or `'right'`
**enableShortcuts**| Boolean| Enable/disable shortcuts| `true`
**showToolbar**| Boolean| Show/hide toolbar| `true`
**theme**| Object| Storybook Theme, see next section| `undefined`
**selectedPanel**| String| Id to select an addon panel| `storybook/actions/panel`
**initialActive**| String| Select the default active tab on Mobile| `sidebar` or `canvas` or `addons`
**sidebar**| Object| Sidebar options, see below| `{ showRoots: false }`
**toolbar**| Object| Modify the tools in the toolbar using the addon id| `{ fullscreen: { hidden: false } }`
The following options are configurable under the `sidebar` namespace:
Name| Type| Description| Example Value
---|---|---|---
**showRoots**| Boolean| Display the top-level nodes as a "root" in the sidebar| `false`
**collapsedRoots**| Array| Set of root node IDs to visually collapse by default| `['misc', 'other']`
**renderLabel**| Function| Create a custom label for tree nodes; must return a ReactNode| `(item, api) => {item.name}`
The following options are configurable under the `toolbar` namespace:
Name| Type| Description| Example Value
---|---|---|---
**[id]**| String| Toggle visibility for a specific toolbar item (e.g. `title`, `zoom`)| `{ hidden: false }`
* * *
##
Storybook hooks
To help streamline addon development and reduce boilerplate code, the API exposes a set of hooks to access Storybook's internals. These hooks are an extension of the `storybook/manager-api` module.
###
useStorybookState
It allows access to Storybook's internal state. Similar to the `useglobals` hook, we recommend optimizing your addon to rely on [`React.memo`](https://react.dev/reference/react/memo), or the following hooks; [`useMemo`](https://react.dev/reference/react/useMemo), [`useCallback`](https://react.dev/reference/react/useCallback) to prevent a high volume of re-render cycles.
my-addon/src/manager.js|ts
import React from 'react';
import { AddonPanel } from 'storybook/internal/components';
import { useStorybookState } from 'storybook/manager-api';
export const Panel = () => {
const state = useStorybookState();
return (
{state.viewMode !== 'docs' ? (
Do something with the documentation
) : (
Show the panel when viewing the story
)}
);
};
###
useStorybookApi
The `useStorybookApi` hook is a convenient helper to allow you full access to the Storybook API methods.
my-addon/manager.js|ts
import React, { useEffect, useCallback } from 'react';
import { useGlobals, useStorybookApi } from 'storybook/manager-api';
import { ToggleButton } from 'storybook/internal/components';
import { ChevronDownIcon } from '@storybook/icons';
export const Panel = () => {
const [globals, updateGlobals] = useGlobals();
const api = useStorybookApi();
const isActive = [true, 'true'].includes(globals[PARAM_KEY]);
const toggleMyTool = useCallback(() => {
updateGlobals({
[PARAM_KEY]: !isActive,
});
}, [isActive]);
useEffect(() => {
api.setAddonShortcut('custom-toolbar-addon', {
label: 'Enable my addon',
defaultShortcut: ['G'],
actionName: 'Toggle',
showInMenu: false,
action: toggleMyTool,
});
}, [api]);
return (
);
};
###
useChannel
Allows setting subscriptions to events and getting the emitter to emit custom events to the channel.
The messages can be listened to on both the iframe and the manager.
my-addon/manager.js|ts
import React from 'react';
import { useChannel } from 'storybook/manager-api';
import { AddonPanel, Button } from 'storybook/internal/components';
import { STORY_CHANGED } from 'storybook/internal/core-events';
export const Panel = () => {
// Creates a Storybook API channel and subscribes to the STORY_CHANGED event
const emit = useChannel({
STORY_CHANGED: (...args) => console.log(...args),
});
return (
);
};
###
useAddonState
The `useAddonState` is a useful hook for addons that require data persistence, either due to Storybook's UI lifecycle or for more complex addons involving multiple types (e.g., toolbars, panels).
my-addon/manager.js|ts
import React from 'react';
import { useAddonState } from 'storybook/manager-api';
import { AddonPanel, Button, ToggleButton } from 'storybook/internal/components';
import { LightningIcon } from '@storybook/icons';
export const Panel = () => {
const [state, setState] = useAddonState('addon-unique-identifier', 'initial state');
return (
);
};
export const Tool = () => {
const [state, setState] = useAddonState('addon-unique-identifier', 'initial state');
return (
setState('Example')}
>
);
};
###
useParameter
The `useParameter` retrieves the current story's parameters. If the parameter's value is not defined, it will automatically default to the second value defined.
my-addon/manager.js|ts
import React from 'react';
import { AddonPanel } from 'storybook/internal/components';
import { useParameter } from 'storybook/manager-api';
export const Panel = () => {
// Connects to Storybook's API and retrieves the value of the custom parameter for the current story
const value = useParameter('custom-parameter', 'initial value');
return (
{value === 'initial value' ? (
The story doesn't contain custom parameters. Defaulting to the initial value.
) : (
You've set {value} as the parameter.
)}
);
};
###
useGlobals
Extremely useful hook for addons that rely on Storybook [Globals](../essentials/toolbars-and-globals). It allows you to obtain and update `global` values. We also recommend optimizing your addon to rely on [`React.memo`](https://react.dev/reference/react/memo), or the following hooks; [`useMemo`](https://react.dev/reference/react/useMemo), [`useCallback`](https://react.dev/reference/react/useCallback) to prevent a high volume of re-render cycles.
my-addon/manager.js|ts
import React from 'react';
import { AddonPanel, Button } from 'storybook/internal/components';
import { useGlobals } from 'storybook/manager-api';
export const Panel = () => {
const [globals, updateGlobals] = useGlobals();
const isActive = globals['my-param-key'] || false; // 👈 Sets visibility based on the global value.
return (
);
};
###
useArgs
Hook that allows you to retrieve or update a story's [`args`](../writing-stories/args).
my-addon/src/manager.js|ts
import { useArgs } from 'storybook/manager-api';
const [args, updateArgs, resetArgs] = useArgs();
// To update one or more args:
updateArgs({ key: 'value' });
// To reset one (or more) args:
resetArgs((argNames: ['key']));
// To reset all args
resetArgs();
**Learn more about the Storybook addon ecosystem**
* [Types of addons](./addon-types) for other types of addons
* [Writing addons](./writing-addons) for the basics of addon development
* [Presets](./writing-presets) for preset development
* [Integration catalog](./integration-catalog) for requirements and available recipes
* API reference to learn about the available APIs
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/addons-api.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/configure-addons
# Page: /docs/addons/configure-addons
# Configure and communicate with an addon
ReactVueAngularWeb ComponentsMore
The addon API is designed for customization. It offers addon authors different ways to configure and communicate with their users' Storybook. Let's look at what these are and their suggested use cases.
##
Preset
Presets offload the burden of configuration from the user to the addon. Preset options are global and are accessible from NodeJS. They're ideal for pre-configuring Webpack loaders, Babel plugins, and other library or framework-specific configurations.
For example, many libraries require that the app be wrapped by a `Provider` which _provides_ data to components down the tree. Presets can describe behavior like adding wrappers automatically, without users having to do any manual configuration. If a user installs an addon that has Presets, the addon can instruct Storybook to wrap all stories in `Provider`. This allows folks to start using your library with Storybook, with just 1 line of config!
For more on presets, see: [Write a preset addon](./writing-presets)
The mechanism for wrapping each story is referred to as a Storybook [decorator](../writing-stories/decorators). They allow you to augment stories with extra rendering functionality or by providing data.
##
Parameters
Parameters are available in the browser and are great for configuring addon behavior globally, at the component level, or at the story level.
For example, the [Pseudo States addon](https://storybook.js.org/addons/storybook-addon-pseudo-states) uses parameters to enable the various pseudo-states. Users can provide global defaults and then override them at the story level.
Use the [`useParameter`](./addons-api#useparameter) hook to access the parameter values within your addon.
export const Hover = {
render: () => ,
parameters: { pseudo: { hover: true } },
};
##
Channels
Channels enable two-way communication between the manager and the preview pane, using a NodeJS [EventEmitter](https://nodejs.org/api/events.html) compatible API. Your addons can plug into specific channels and respond to these events.
For example, [Actions](https://storybook.js.org/docs/essentials/actions) captures user events and displays their data in a panel.
Use the [`useChannel`](./addons-api#usechannel) hook to access the channel data within your addon.
For a complete example, check out [storybookjs/addon-kit/withRoundTrip.ts](https://github.com/storybookjs/addon-kit/blob/main/src/withRoundTrip.ts)
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/configure-addons.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/install-addons
# Page: /docs/addons/install-addons
# Install addons
ReactVueAngularWeb ComponentsMore
Storybook has [hundreds of reusable addons](https://storybook.js.org/integrations) packaged as NPM modules. Let's walk through how to extend Storybook by installing and registering addons.
##
Automatic installation
Storybook includes a [`storybook add`](../api/cli-options#add) command to automate the setup of addons. Several community-led addons can be added using this command, except for preset addons. We encourage you to read the addon's documentation to learn more about its installation process.
Run the `storybook add` command using your chosen package manager, and the CLI will update your Storybook configuration to include the addon and install any necessary dependencies.
npm
npx storybook@latest add @storybook/addon-a11y
⚠️
If you're attempting to install multiple addons at once, it will only install the first addon that was specified. This is a known limitation of the current implementation and will be addressed in a future release.
###
Manual installation
Storybook addons are always added through the [`addons`](../api/main-config/main-config-addons) configuration array in [`.storybook/main.js|ts`](../configure). The following example shows how to manually add the [Accessibility addon](https://storybook.js.org/addons/@storybook/addon-a11y) to Storybook.
Run the following command with your package manager of choice to install the addon.
npm
npm install @storybook/addon-a11y --save-dev
Next, update `.storybook/main.js|ts` to the following:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite, angular, etc.)
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
// Other Storybook addons
'@storybook/addon-a11y', //👈 The a11y addon goes here
],
};
export default config;
When you run Storybook, the accessibility testing addon will be enabled.

###
Removing addons
To remove an addon from Storybook, you can choose to manually uninstall it and remove it from the configuration file (i.e., [`.storybook/main.js|ts`](../configure)) or opt-in to do it automatically via the CLI with the [`remove`](../api/cli-options#remove) command. For example, to remove the [Accessibility addon](https://storybook.js.org/addons/@storybook/addon-a11y) from Storybook with the CLI, run the following command:
npm
npx storybook@latest remove @storybook/addon-a11y
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/install-addons.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/integration-catalog
# Page: /docs/addons/integration-catalog
# Add to the integration catalog
ReactVueAngularWeb ComponentsMore
Storybook has two types of integrations, addons and recipes, which are listed in the [integration catalog](https://storybook.js.org/integrations/).
##
Addons
Storybook addons are distributed via npm. The catalog is populated by querying npm's registry for Storybook-specific metadata in `package.json`.
Add your addon to the catalog by publishing a npm package that follows these requirements:
* `package.json` with [module information](./writing-addons#setup) and addon metadata
* `README.md` file with installation and configuration instructions
* `/dist` directory containing transpiled ES5 code
* `preset.js` file written as an ES5 module at the root level
💡
Get a refresher on how to [write a Storybook addon](./writing-addons).
###
Addon metadata
We rely on metadata to organize your addon in the catalog. You must add the `storybook-addon` as the first keyword, followed by your addon's category. Additional keywords will be used in search and as tags.
Property| Description| Example
---|---|---
`name`| Addon package name| storybook-addon-example
`description`| Addon description| Outline all elements with CSS to help with layout placement and alignment
`author`| Name of the author| winkerVSbecks
`keywords`| List of keywords to describe the addon| `["storybook-addon","style","debug"]`
`repository`| Addon repository| `{"type": "git","url": "https://github.com/someone/my-addon" }`
Customize your addon's appearance by adding the `storybook` property with the following fields.
Property| Description| Example
---|---|---
`displayName`| Display name| Example
`icon`| Link to custom icon for the addon (SVG are not supported)|
`unsupportedFrameworks`| List of unsupported frameworks| `["vue"]`
`supportedFrameworks`| List of supported frameworks| `["react", "angular"]`
Use the list below as a reference when filling in the values for both the `supportedFrameworks` and `unsupportedFrameworks` fields.
* react
* vue
* angular
* web-components
* ember
* html
* svelte
* preact
* react-native
💡
Make sure to copy each item **exactly** as listed so that we can properly index your addon in our catalog.
package.json
{
"name": "storybook-addon-example",
"version": "1.0.0",
"description": "Outline all elements with CSS to help with layout placement and alignment",
"repository": {
"type": "git",
"url": "https://github.com/chromaui/storybook-addon-example"
},
"author": "winkerVSbecks",
"keywords": ["storybook-addon", "style", "debug", "layout", "css"],
"storybook": {
"displayName": "Outline",
"unsupportedFrameworks": ["vue"],
"supportedFrameworks": ["react", "angular"],
"icon": "https://yoursite.com/addon-icon.png"
}
}
The `package.json` above appears like below in the catalog. See an example of a production package.json [here](https://github.com/chromaui/storybook-outline/blob/main/package.json).

####
How long does it take for my addon to show up in the catalog?
Once you publish the addon, it will appear in the catalog. There may be a delay between the time you publish your addon and when it's listed in the catalog. If your addon doesn't show up within 24 hours, [open an issue](https://github.com/storybookjs/frontpage/issues).
##
Recipes
Recipes are a set of instructions to integrate third-party libraries into Storybook in cases where an addon does not exist or the integration requires some manual effort.
###
Who owns them?
Recipes are written and maintained by the Storybook team. We create recipes based on community popularity, tool maturity, and stability of the integration. Our goal is to ensure that recipes continue to work over time.
Not finding the recipe that you want? If it's popular in the community, our docs team will write one. In the mean time, try searching for a solution — it's likely that someone has the same requirements as you do. You can also help us out by writing recipes on your own site which speeds up the research process.
###
Request a recipe
If you'd like to request a recipe, open a [new discussion](https://github.com/storybookjs/storybook/discussions/new?category=ideas) in our GitHub repo. We'll review your request, and if it's popular, we'll add it to our backlog and prioritize it.
**Learn more about the Storybook addon ecosystem**
* [Types of addons](./addon-types) for other types of addons
* [Writing addons](./writing-addons) for the basics of addon development
* [Presets](./writing-presets) for preset development
* Integration catalog for requirements and available recipes
* [API reference](./addons-api) to learn about the available APIs
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/integration-catalog.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/writing-addons
# Page: /docs/addons/writing-addons
# Write an addon
ReactVueAngularWeb ComponentsMore
Storybook addons are a powerful way to extend Storybook's functionality and customize the development experience. They can be used to add new features, customize the UI, or integrate with third-party tools.
##
What are we going to build?
This reference guide is to help you develop a mental model for how Storybook addons work by building a simple addon based on the popular [Outline addon](https://storybook.js.org/addons/@storybook/addon-outline/) (which is the historical basis for the built-in [outline feature](../essentials/measure-and-outline#outline)). Throughout this guide, you'll learn how addons are structured, Storybook's APIs, how to test your addon locally, and how to publish it.

##
Addon anatomy
There are two main categories of addons, each with its role:
* **UI-based** : These addons are responsible for customizing the interface, enabling shortcuts for common tasks, or displaying additional information in the UI.
* **Presets** : [These](./writing-presets) are pre-configured settings or configurations that enable developers to quickly set up and customize their environment with a specific set of features, functionality, or technology.
###
UI-based addons
The addon built in this guide is a UI-based addon, specifically a [toolbar](./addon-types#toolbars) addon, enabling users to draw outlines around each element in the story through a shortcut or click of a button. UI addons can create other types of UI elements, each with its function: [panels](./addon-types#panels) and [tabs](./addon-types#tabs), providing users with various ways to interact with the UI.
ToolbarPanelTab
src/Tool.tsx
import React, { memo, useCallback, useEffect } from 'react';
import { useGlobals, useStorybookApi } from 'storybook/manager-api';
import { ToggleButton } from 'storybook/internal/components';
import { LightningIcon } from '@storybook/icons';
import { ADDON_ID, PARAM_KEY, TOOL_ID } from './constants';
export const Tool = memo(function MyAddonSelector() {
const [globals, updateGlobals] = useGlobals();
const api = useStorybookApi();
const isActive = [true, 'true'].includes(globals[PARAM_KEY]);
const toggleMyTool = useCallback(() => {
updateGlobals({
[PARAM_KEY]: !isActive,
});
}, [isActive]);
useEffect(() => {
api.setAddonShortcut(ADDON_ID, {
label: 'Toggle Outline',
defaultShortcut: ['alt', 'O'],
actionName: 'outline',
showInMenu: false,
action: toggleMyTool,
});
}, [toggleMyTool, api]);
return (
);
});
##
Setup
To create your first addon, you're going to use the [Addon Kit](https://github.com/storybookjs/addon-kit), a ready-to-use template featuring all the required building blocks, dependencies and configurations to help you get started building your addon. In the Addon Kit repository, click the **Use this template** button to create a new repository based on the Addon Kit's code.
Clone the repository you just created and install its dependencies. When the installation process finishes, you will be prompted with questions to configure your addon. Answer them, and when you're ready to start building your addon, run the following command to start Storybook in development mode and develop your addon in watch mode:
npm
npm run start
ℹ️
The Addon Kit uses [Typescript](https://www.typescriptlang.org/) by default. If you want to use JavaScript instead, you can run the `eject-ts` command to convert the project to JavaScript.
###
Understanding the build system
Addons built in the Storybook ecosystem rely on [tsup](https://tsup.egoist.dev/), a fast, zero-config bundler powered by [esbuild](https://esbuild.github.io/) to transpile your addon's code into modern JavaScript that can run in the browser. Out of the box, the Addon Kit comes with a pre-configured `tsup` configuration file that you can use to customize the build process of your addon.
When the build scripts run, it will look for the configuration file and pre-bundle the addon's code based on the configuration provided. Addons can interact with Storybook in various ways. They can define presets to modify the configuration, add behavior to the manager UI, or add behavior to the preview iframe. These different use cases require different bundle outputs because they target different runtimes and environments. Presets are executed in a Node environment. Storybook's manager and preview environments provide certain packages in the global scope, so addons don't need to bundle them or include them as dependencies in their `package.json` file.
The `tsup` configuration handles these complexities by default, but you can customize it according to their requirements. For a detailed explanation of the bundling techniques used, please refer to [the README of the addon-kit](https://github.com/storybookjs/addon-kit#bundling), and check out the default `tsup` configuration [here](https://github.com/storybookjs/addon-kit/blob/main/tsup.config.ts).
##
Register the addon
By default, code for the UI-based addons is located in one of the following files, depending on the type of addon built: **`src/Tool.tsx`** , **`src/Panel.tsx`** , or **`src/Tab.tsx`**. Since we're building a toolbar addon, we can safely remove the `Panel` and `Tab` files and update the remaining file to the following:
src/Tool.tsx
import React, { memo, useCallback, useEffect } from 'react';
import { useGlobals, useStorybookApi } from 'storybook/manager-api';
import { ToggleButton } from 'storybook/internal/components';
import { LightningIcon } from '@storybook/icons';
import { ADDON_ID, PARAM_KEY, TOOL_ID } from './constants';
export const Tool = memo(function MyAddonSelector() {
const [globals, updateGlobals] = useGlobals();
const api = useStorybookApi();
const isActive = [true, 'true'].includes(globals[PARAM_KEY]);
const toggleMyTool = useCallback(() => {
updateGlobals({
[PARAM_KEY]: !isActive,
});
}, [isActive]);
useEffect(() => {
api.setAddonShortcut(ADDON_ID, {
label: 'Toggle Addon [8]',
defaultShortcut: ['8'],
actionName: 'myaddon',
showInMenu: false,
action: toggleMyTool,
});
}, [toggleMyTool, api]);
return (
);
});
Going through the code blocks in sequence:
src/Tool.tsx
import { useGlobals, useStorybookApi } from 'storybook/manager-api';
import { Button } from 'storybook/internal/components';
import { LightningIcon } from '@storybook/icons';
The [`useGlobals`](./addons-api#useglobals) and [`useStorybookApi`](./addons-api#usestorybookapi) hooks from the `manager-api` package are used to access the Storybook's APIs, allowing users to interact with the addon, such as enabling or disabling it.
The `Button` component from the `storybook/internal/components` module can be used to render the buttons in the toolbar. The [`@storybook/icons`](https://github.com/storybookjs/icons) package provides a large set of appropriately sized and styled icons to choose from.
src/Tool.tsx
export const Tool = memo(function MyAddonSelector() {
const [globals, updateGlobals] = useGlobals();
const api = useStorybookApi();
const isActive = [true, 'true'].includes(globals[PARAM_KEY]);
const toggleMyTool = useCallback(() => {
updateGlobals({
[PARAM_KEY]: !isActive,
});
}, [isActive]);
useEffect(() => {
api.setAddonShortcut(ADDON_ID, {
label: 'Toggle Addon [8]',
defaultShortcut: ['8'],
actionName: 'myaddon',
showInMenu: false,
action: toggleMyTool,
});
}, [toggleMyTool, api]);
return (
);
});
The `Tool` component is the entry point of the addon. It renders the UI elements in the toolbar, registers a keyboard shortcut, and handles the logic to enable and disable the addon.
Moving onto the manager, here we register the addon with Storybook using a unique name and identifier. Since we've removed the `Panel` and `Tab` files, we'll need to adjust the file to only reference the addon we're building.
src/manager.ts
import { addons, types } from 'storybook/manager-api';
import { ADDON_ID, TOOL_ID } from './constants';
import { Tool } from './Tool';
// Register the addon
addons.register(ADDON_ID, () => {
// Register the tool
addons.add(TOOL_ID, {
type: types.TOOL,
title: 'My addon',
match: ({ tabId, viewMode }) => !tabId && viewMode === 'story',
render: Tool,
});
});
###
Conditionally render the addon
Notice the `match` property. It allows you to control the view mode (story or docs) and tab (the story canvas or [custom tabs](./addon-types#tabs)) where the toolbar addon is visible. For example:
* `({ tabId }) => tabId === 'my-addon/tab'` will show your addon when viewing the tab with the ID `my-addon/tab`.
* `({ viewMode }) => viewMode === 'story'` will show your addon when viewing a story in the canvas.
* `({ viewMode }) => viewMode === 'docs'` will show your addon when viewing the documentation for a component.
* `({ tabId, viewMode }) => !tabId && viewMode === 'story'` will show your addon when viewing a story in the canvas and not in a custom tab (i.e. when `tabId === undefined`).
Run the `start` script to build and start Storybook and verify that the addon is registered correctly and showing in the UI.

###
Style the addon
In Storybook, applying styles for addons is considered a side-effect. Therefore, we'll need to make some changes to our addon to allow it to use the styles when it is active and remove them when it's disabled. We're going to rely on two of Storybook's features to handle this: [decorators](../writing-stories/decorators) and [globals](../essentials/toolbars-and-globals#globals). To handle the CSS logic, we must include some helper functions to inject and remove the stylesheets from the DOM. Start by creating the helper file with the following content:
src/helpers.ts
import { global } from '@storybook/global';
export const clearStyles = (selector: string | string[]) => {
const selectors = Array.isArray(selector) ? selector : [selector];
selectors.forEach(clearStyle);
};
const clearStyle = (input: string | string[]) => {
const selector = typeof input === 'string' ? input : input.join('');
const element = global.document.getElementById(selector);
if (element && element.parentElement) {
element.parentElement.removeChild(element);
}
};
export const addOutlineStyles = (selector: string, css: string) => {
const existingStyle = global.document.getElementById(selector);
if (existingStyle) {
if (existingStyle.innerHTML !== css) {
existingStyle.innerHTML = css;
}
} else {
const style = global.document.createElement('style');
style.setAttribute('id', selector);
style.innerHTML = css;
global.document.head.appendChild(style);
}
};
Next, create the file with the styles we want to inject with the following content:
src/OutlineCSS.ts
import { dedent } from 'ts-dedent';
export default function outlineCSS(selector: string) {
return dedent/* css */ `
${selector} body {
outline: 1px solid #2980b9 !important;
}
${selector} article {
outline: 1px solid #3498db !important;
}
${selector} nav {
outline: 1px solid #0088c3 !important;
}
${selector} aside {
outline: 1px solid #33a0ce !important;
}
${selector} section {
outline: 1px solid #66b8da !important;
}
${selector} header {
outline: 1px solid #99cfe7 !important;
}
${selector} footer {
outline: 1px solid #cce7f3 !important;
}
${selector} h1 {
outline: 1px solid #162544 !important;
}
${selector} h2 {
outline: 1px solid #314e6e !important;
}
${selector} h3 {
outline: 1px solid #3e5e85 !important;
}
${selector} h4 {
outline: 1px solid #449baf !important;
}
${selector} h5 {
outline: 1px solid #c7d1cb !important;
}
${selector} h6 {
outline: 1px solid #4371d0 !important;
}
${selector} main {
outline: 1px solid #2f4f90 !important;
}
${selector} address {
outline: 1px solid #1a2c51 !important;
}
${selector} div {
outline: 1px solid #036cdb !important;
}
${selector} p {
outline: 1px solid #ac050b !important;
}
${selector} hr {
outline: 1px solid #ff063f !important;
}
${selector} pre {
outline: 1px solid #850440 !important;
}
${selector} blockquote {
outline: 1px solid #f1b8e7 !important;
}
${selector} ol {
outline: 1px solid #ff050c !important;
}
${selector} ul {
outline: 1px solid #d90416 !important;
}
${selector} li {
outline: 1px solid #d90416 !important;
}
${selector} dl {
outline: 1px solid #fd3427 !important;
}
${selector} dt {
outline: 1px solid #ff0043 !important;
}
${selector} dd {
outline: 1px solid #e80174 !important;
}
${selector} figure {
outline: 1px solid #ff00bb !important;
}
${selector} figcaption {
outline: 1px solid #bf0032 !important;
}
${selector} table {
outline: 1px solid #00cc99 !important;
}
${selector} caption {
outline: 1px solid #37ffc4 !important;
}
${selector} thead {
outline: 1px solid #98daca !important;
}
${selector} tbody {
outline: 1px solid #64a7a0 !important;
}
${selector} tfoot {
outline: 1px solid #22746b !important;
}
${selector} tr {
outline: 1px solid #86c0b2 !important;
}
${selector} th {
outline: 1px solid #a1e7d6 !important;
}
${selector} td {
outline: 1px solid #3f5a54 !important;
}
${selector} col {
outline: 1px solid #6c9a8f !important;
}
${selector} colgroup {
outline: 1px solid #6c9a9d !important;
}
${selector} button {
outline: 1px solid #da8301 !important;
}
${selector} datalist {
outline: 1px solid #c06000 !important;
}
${selector} fieldset {
outline: 1px solid #d95100 !important;
}
${selector} form {
outline: 1px solid #d23600 !important;
}
${selector} input {
outline: 1px solid #fca600 !important;
}
${selector} keygen {
outline: 1px solid #b31e00 !important;
}
${selector} label {
outline: 1px solid #ee8900 !important;
}
${selector} legend {
outline: 1px solid #de6d00 !important;
}
${selector} meter {
outline: 1px solid #e8630c !important;
}
${selector} optgroup {
outline: 1px solid #b33600 !important;
}
${selector} option {
outline: 1px solid #ff8a00 !important;
}
${selector} output {
outline: 1px solid #ff9619 !important;
}
${selector} progress {
outline: 1px solid #e57c00 !important;
}
${selector} select {
outline: 1px solid #e26e0f !important;
}
${selector} textarea {
outline: 1px solid #cc5400 !important;
}
${selector} details {
outline: 1px solid #33848f !important;
}
${selector} summary {
outline: 1px solid #60a1a6 !important;
}
${selector} command {
outline: 1px solid #438da1 !important;
}
${selector} menu {
outline: 1px solid #449da6 !important;
}
${selector} del {
outline: 1px solid #bf0000 !important;
}
${selector} ins {
outline: 1px solid #400000 !important;
}
${selector} img {
outline: 1px solid #22746b !important;
}
${selector} iframe {
outline: 1px solid #64a7a0 !important;
}
${selector} embed {
outline: 1px solid #98daca !important;
}
${selector} object {
outline: 1px solid #00cc99 !important;
}
${selector} param {
outline: 1px solid #37ffc4 !important;
}
${selector} video {
outline: 1px solid #6ee866 !important;
}
${selector} audio {
outline: 1px solid #027353 !important;
}
${selector} source {
outline: 1px solid #012426 !important;
}
${selector} canvas {
outline: 1px solid #a2f570 !important;
}
${selector} track {
outline: 1px solid #59a600 !important;
}
${selector} map {
outline: 1px solid #7be500 !important;
}
${selector} area {
outline: 1px solid #305900 !important;
}
${selector} a {
outline: 1px solid #ff62ab !important;
}
${selector} em {
outline: 1px solid #800b41 !important;
}
${selector} strong {
outline: 1px solid #ff1583 !important;
}
${selector} i {
outline: 1px solid #803156 !important;
}
${selector} b {
outline: 1px solid #cc1169 !important;
}
${selector} u {
outline: 1px solid #ff0430 !important;
}
${selector} s {
outline: 1px solid #f805e3 !important;
}
${selector} small {
outline: 1px solid #d107b2 !important;
}
${selector} abbr {
outline: 1px solid #4a0263 !important;
}
${selector} q {
outline: 1px solid #240018 !important;
}
${selector} cite {
outline: 1px solid #64003c !important;
}
${selector} dfn {
outline: 1px solid #b4005a !important;
}
${selector} sub {
outline: 1px solid #dba0c8 !important;
}
${selector} sup {
outline: 1px solid #cc0256 !important;
}
${selector} time {
outline: 1px solid #d6606d !important;
}
${selector} code {
outline: 1px solid #e04251 !important;
}
${selector} kbd {
outline: 1px solid #5e001f !important;
}
${selector} samp {
outline: 1px solid #9c0033 !important;
}
${selector} var {
outline: 1px solid #d90047 !important;
}
${selector} mark {
outline: 1px solid #ff0053 !important;
}
${selector} bdi {
outline: 1px solid #bf3668 !important;
}
${selector} bdo {
outline: 1px solid #6f1400 !important;
}
${selector} ruby {
outline: 1px solid #ff7b93 !important;
}
${selector} rt {
outline: 1px solid #ff2f54 !important;
}
${selector} rp {
outline: 1px solid #803e49 !important;
}
${selector} span {
outline: 1px solid #cc2643 !important;
}
${selector} br {
outline: 1px solid #db687d !important;
}
${selector} wbr {
outline: 1px solid #db175b !important;
}`;
}
Since the addon can be active in both the story and documentation modes, the DOM node for Storybook's preview `iframe` is different in these two modes. In fact, Storybook renders multiple story previews on one page when in documentation mode. Therefore, we'll need to choose the correct selector for the DOM node where the styles will be injected and ensure the CSS is scoped to that particular selector. That mechanism is provided as an example within the `src/withGlobals.ts` file, which we'll use to connect the styling and helper functions to the addon logic. Update the file to the following:
src/withGlobals.ts
import type {
Renderer,
PartialStoryFn as StoryFunction,
StoryContext,
} from 'storybook/internal/types';
import { useEffect, useMemo, useGlobals } from 'storybook/preview-api';
import { PARAM_KEY } from './constants';
import { clearStyles, addOutlineStyles } from './helpers';
import outlineCSS from './outlineCSS';
export const withGlobals = (StoryFn: StoryFunction, context: StoryContext) => {
const [globals] = useGlobals();
const isActive = [true, 'true'].includes(globals[PARAM_KEY]);
// Is the addon being used in the docs panel
const isInDocs = context.viewMode === 'docs';
const outlineStyles = useMemo(() => {
const selector = isInDocs ? `#anchor--${context.id} .docs-story` : '.sb-show-main';
return outlineCSS(selector);
}, [context.id]);
useEffect(() => {
const selectorId = isInDocs ? `my-addon-docs-${context.id}` : `my-addon`;
if (!isActive) {
clearStyles(selectorId);
return;
}
addOutlineStyles(selectorId, outlineStyles);
return () => {
clearStyles(selectorId);
};
}, [isActive, outlineStyles, context.id]);
return StoryFn();
};
##
Packaging and publishing
Storybook addons, similar to most packages in the JavaScript ecosystem, are distributed as NPM packages. However, they have specific criteria that need to be met to be published to NPM and crawled by the integration catalog:
1. Have a `dist` folder with the transpiled code.
2. A `package.json` file declaring:
* Module-related information
* Integration catalog metadata
###
Module Metadata
The first category of metadata is related to the addon itself. This includes the entry for the module, which files to include when the addon is published. And the required configuration to integrate the addon with Storybook, allowing it to be used by its consumers.
package.json
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"node": "./dist/index.js",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./manager": "./dist/manager.mjs",
"./preview": "./dist/preview.mjs",
"./package.json": "./package.json"
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": ["dist/**/*", "README.md", "*.js", "*.d.ts"],
"devDependencies": {
"@storybook/addon-docs": "^9.0.0",
"storybook": "^9.0.0"
},
"bundler": {
"exportEntries": ["src/index.ts"],
"managerEntries": ["src/manager.ts"],
"previewEntries": ["src/preview.ts"]
}
}
###
Integration Catalog Metadata
The second metadata category is related to the [integration catalog](https://storybook.js.org/integrations). Most of this information is already pre-configured by the Addon Kit. However, items like the display name, icon, and frameworks must be configured via the `storybook` property to be displayed in the catalog.
package.json
{
"name": "my-storybook-addon",
"version": "1.0.0",
"description": "My first storybook addon",
"author": "Your Name",
"storybook": {
"displayName": "My Storybook Addon",
"unsupportedFrameworks": ["react-native"],
"icon": "https://yoursite.com/link-to-your-icon.png"
},
"keywords": ["storybook-addon", "appearance", "style", "css", "layout", "debug"]
}
ℹ️
The `storybook` configuration element includes additional properties that help customize the addon's searchability and indexing. For more information, see the [Integration catalog documentation](./integration-catalog).
One essential item to note is the `keywords` property as it maps to the catalog's tag system. Adding the `storybook-addon` keyword ensures that the addon is discoverable in the catalog when searching for addons. The remaining keywords help with the searchability and categorization of the addon.
###
Publishing to NPM
Once you're ready to publish your addon to NPM, the Addon Kit comes pre-configured with the [Auto](https://github.com/intuit/auto) package for release management. It generates a changelog and uploads the package to NPM and GitHub automatically. Therefore, you need to configure access to both.
1. Authenticate using [npm adduser](https://docs.npmjs.com/cli/v9/commands/npm-adduser)
2. Generate a [access token](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-access-tokens) with both `read` and `publish` permissions.
3. Create a [personal access token](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token) with `repo` and `workflow` scoped permissions.
4. Create a `.env` file in the root of your project and add the following:
GH_TOKEN=value_you_just_got_from_github
NPM_TOKEN=value_you_just_got_from_npm
Next, run the following command to create labels on GitHub. You'll use these labels to categorize changes to the package.
npx auto create-labels
Finally, run the following command to create a release for your addon. This will build and package the addon code, bump the version, push the release into GitHub and npm, and generate a changelog.
npm
npm run release
###
CI automation
By default, the Addon Kit comes pre-configured with a GitHub Actions workflow, enabling you to automate the release management process. This ensures that the package is always up to date with the latest changes and that the changelog is updated accordingly. However, you'll need additional configuration to use your NPM and GitHub tokens to publish the package successfully. In your repository, click the **Settings** tab, then the **Secrets and variables** dropdown, followed by the **Actions** item. You should see the following screen:

Then, click the **New repository secret** , name it `NPM_TOKEN`, and paste the token you generated earlier. Whenever you merge a pull request to the default branch, the workflow will run and publish a new release, automatically incrementing the version number and updating the changelog.
**Learn more about the Storybook addon ecosystem**
* [Types of addons](./addon-types) for other types of addons
* Writing addons for the basics of addon development
* [Presets](./writing-presets) for preset development
* [Integration catalog](./integration-catalog) for requirements and available recipes
* [API reference](./addons-api) to learn about the available APIs
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/writing-addons.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons/writing-presets
# Page: /docs/addons/writing-presets
# Write a preset addon
ReactVueAngularWeb ComponentsMore
Storybook presets are pre-configured settings or configurations that enable developers quickly set up and customize their environment with a specific set of features, functionalities, or integrations.
##
How presets work
Preset addons allow developers to compose various configuration options and plugins via APIs to integrate with Storybook and customize its behavior and functionality. Typically, presets are separated into two files, each with its specific role.
###
Local presets
This type of preset allows you to encapsulate and organize configurations specific to the addon, including [builder](../builders) support, [Babel](https://babeljs.io/), or third-party integrations. For example:
example-addon/src/preset.ts
Typescript
import { webpackFinal as webpack } from './webpack/webpackFinal';
import { viteFinal as vite } from './vite/viteFinal';
import { babelDefault as babel } from './babel/babelDefault';
export const webpackFinal = webpack as any;
export const viteFinal = vite as any;
export const babelDefault = babel as any;
###
Root-level presets
This type of preset is user-facing and responsible for registering the addon without any additional configuration from the user by bundling Storybook-related features (e.g., [parameters](../writing-stories/parameters)) via the [`previewAnnotations`](../api/main-config/main-config-preview-annotations) and UI related features (e.g., addons) via the `managerEntries` API. For example:
example-addon/preset.js
export const previewAnnotations = [import.meta.resolve('./dist/preview')];
export const managerEntries = [import.meta.resolve('./dist/manager')];
export * from './dist/preset.js';
##
Presets API
When writing a preset, you can access a select set of APIs to interact with the Storybook environment, including the supported builders (e.g., Webpack, Vite), the Storybook configuration, and UI. Below are the available APIs you can use when writing a preset addon.
###
Babel
To customize Storybook's Babel configuration and add support for additional features, you can use the [`babelDefault`](../api/main-config/main-config-babel-default) API. It will apply the provided configuration ahead of any other user presets, which can be further customized by the end user via the [`babel`](../api/main-config/main-config-babel) configuration option. For example:
example-addon/src/babel/babelDefault.ts
Typescript
import { TransformOptions } from '@babel/core';
export function babelDefault(config: TransformOptions) {
return {
...config,
plugins: [
...config.plugins,
[import.meta.resolve('@babel/plugin-transform-react-jsx'), {}, 'preset'],
],
};
}
ℹ️
The Babel configuration is only applied to frameworks that use Babel internally. If you enable it for a framework that uses a different compiler, like [SWC](https://swc.rs/) or [esbuild](https://esbuild.github.io/), it will be ignored.
###
Builders
By default, Storybook provides support for the leading industry builders, including [Webpack](../builders/webpack) and [Vite](../builders/vite). If you need additional features for any of these builders, you can use APIs to extend the builder configuration based on your specific needs.
####
Vite
If you are creating a preset and want to include Vite support, the `viteFinal` API can be used to modify the default configuration and enable additional features. For example:
example-addon/src/vite/viteFinal.ts
Typescript
export function ViteFinal(config: any, options: any = {}) {
config.plugins.push(
new MyCustomPlugin({
someOption: true,
}),
);
return config;
}
####
Webpack
To customize the Webpack configuration in Storybook to add support for additional file types, apply specific loaders, configure plugins, or make any other necessary modifications, you can use the `webpackFinal` API. Once invoked, it will extend the default Webpack configuration with the provided configuration. An example of this would be:
example-addon/src/webpack/webpackFinal.ts
Typescript
import { fileURLtoPath } from 'node:url';
import type { Configuration as WebpackConfig } from 'webpack';
export function webpackFinal(config: WebpackConfig, options: any = {}) {
const rules = [
...(config.module?.rules || []),
{
test: /\.custom-file$/,
loader: fileURLToPath(import.meta.resolve(`custom-loader`)),
},
];
config.module.rules = rules;
return config;
}
###
ManagerEntries
If you're writing a preset that loads third-party addons, which you may not have control over, but require access to specific features or additional configuration, you can use the `managerEntries` API. For example:
example-addon/preset.js
export const managerEntries = (entry = []) => {
return [...entry, import.meta.resolve('path-to-third-party-addon')];
};
###
PreviewAnnotations
If you need additional settings to render stories for a preset, like [decorators](../writing-stories/decorators) or [parameters](../writing-stories/parameters), you can use the `previewAnnotations` API. For example, to apply a decorator to all stories, create a preview file that includes the decorator and make it available to the preset as follows:
CSF 3CSF Next 🧪
example-addon/src/preview.ts
Typescript
import type { Renderer, ProjectAnnotations } from 'storybook/internal/types';
import { PARAM_KEY } from './constants';
import { CustomDecorator } from './decorators';
const preview: ProjectAnnotations = {
decorators: [CustomDecorator],
globals: {
[PARAM_KEY]: false,
},
};
export default preview;
##
Advanced configuration
The presets API is designed to be flexible and allow you to customize Storybook to your specific needs, including using presets for more advanced use cases without publishing them. In such cases, you can rely on a private preset. These private presets contain configuration options meant for development purposes and not for end-users. The `.storybook/main.js|ts` file is an example of such a private preset that empowers you to modify the behavior and functionality of Storybook.
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, angular, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
viteFinal: async (config, options) => {
// Update config here
return config;
},
webpackFinal: async (config, options) => {
// Change webpack config
return config;
},
babel: async (config, options) => {
return config;
},
};
export default config;
###
Addons
For addon consumers, the `managerEntries` API can be too technical, making it difficult to use. To make it easier to add addons to Storybook, the preset API provides the [`addons`](../api/main-config/main-config-addons) API, which accepts an array of addon names and will automatically load them for you. For example:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
addons: [
// Other Storybook addons
'@storybook/addon-a11y',
],
};
export default config;
The array of values supports references to additional presets and addons that should be included in the manager. Storybook will automatically detect whether the provided value is a preset or an addon and load it accordingly.
###
Entries
Entries are the place to register entry points for the preview. This feature can be utilized to create a configure-storybook preset that automatically loads all `*.stories.js` files into Storybook, eliminating the need for users to copy-paste the same configuration repeatedly.
###
UI configuration
The Storybook preset API also provides access to the UI configuration, including the `head` and `body` HTML elements of the preview, configured by the [`previewHead`](../api/main-config/main-config-preview-head) and [`previewBody`](../api/main-config/main-config-preview-body) APIs. Both allow you to set up Storybook in a way that is similar to using the [`preview-head.html`](../configure/story-rendering#adding-to-head) and [`preview-body.html`](../configure/story-rendering#adding-to-body) files. These methods accept a string and return a modified version, injecting the provided content into the HTML element.
Body (CSF 3)Head (CSF 3)Body (CSF Next 🧪)Head (CSF Next 🧪)
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
previewBody: (body) => `
${body}
${
process.env.ANALYTICS_ID ? '' : ''
}
`,
};
export default config;
Additionally, if you need to customize the manager (i.e., where Storybook’s search, navigation, toolbars, and addons render), you can use the [`managerHead`](../api/main-config/main-config-manager-head) to modify the UI, similar to how you would do it with the `manager-head.html` file. For example:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
managerHead: (head) => `
${head}
`,
};
export default config;
However, if you need, you can also customize the template used by Storybook to render the UI. To do so, you can use the `previewMainTemplate` API and provide a reference for a custom template created as a `ejs` file. For an example of how to do this, see the [template](https://github.com/storybookjs/storybook/blob/next/code/builders/builder-webpack5/templates/preview.ejs) used by the Webpack 5 builder.
##
Troubleshooting
###
Storybook doesn't load files in my preset
As Storybook relies on [esbuild](https://esbuild.github.io/) instead of Webpack to build the UI, presets that depend on the `managerWebpack` API to configure the manager or load additional files other than CSS or images will no longer work. We recommend removing it from your preset and adjusting your configuration to convert any additional files to JavaScript.
**Learn more about the Storybook addon ecosystem**
* [Types of addons](./addon-types) for other types of addons
* [Writing addons](./writing-addons) for the basics of addon development
* Presets for preset development
* [Integration catalog](./integration-catalog) for requirements and available recipes
* [API reference](./addons-api) to learn about the available APIs
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/writing-presets.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/addons
# Page: /docs/addons
# Introduction to addons
Addons extend Storybook with features and integrations that are not built into the core. Most Storybook features are implemented as addons. For instance: [documentation](./writing-docs), [accessibility testing](./writing-tests/accessibility-testing), [interactive controls](./essentials/controls), among others. The [addon API](./addons/addons-api) makes it easy for you to configure and customize Storybook in new ways. There are countless addons made by the community that unlocks time-saving workflows.
Browse our [addon catalog](https://storybook.js.org/addons) to install an existing addon or as inspiration for your own addon.
##
Storybook basics
Before writing your first [addon](https://storybook.js.org/addons), let’s take a look at the basics of Storybook’s architecture. While Storybook presents a unified user interface, under the hood it’s divided down the middle into **Manager** and **Preview**.
The **Manager** is the UI responsible for rendering the:
* 🔍 Search
* 🧭 Navigation
* 🔗 Toolbars
* 📦 Addons
The **Preview** area is an `iframe` where your stories are rendered.

Because both elements run in their own separate `iframes`, they use a communication channel to keep in sync. For example, when you select a story in the Manager an event is dispatched across the channel notifying the Preview to render the story.
##
Anatomy of an addon
Storybook addons allow you to extend what's already possible with Storybook, everything from the [user interface](./addons/addon-types) to the [API](./addons/addons-api). Each one is classified into two broader categories.
###
UI-based addons
[UI-based addons](./addons/addon-types#ui-based-addons) focus on customizing Storybook's user interface to extend your development workflow. Examples of UI-based addons include: [Controls](./essentials/controls), [Docs](./writing-docs) and [Accessibility](./writing-tests/accessibility-testing).
[Learn how to write an addon »](./addons/writing-addons)
###
Preset addons
[Preset addons](./addons/addon-types#preset-addons) help you integrate Storybook with other technologies and libraries. An examples of a preset addons is [preset-create-react-app](https://github.com/storybookjs/presets/tree/master/packages/preset-create-react-app).
[Learn how to write a preset addon »](./addons/writing-presets)
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/addons/index.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/api/arg-types
# Page: /docs/api/arg-types
# ArgTypes
ReactVueAngularWeb ComponentsMore
ArgTypes specify the behavior of [args](../writing-stories/args). By specifying the type of an arg, you constrain the values that it can accept and provide information about args that are not explicitly set (i.e., description).
You can also use argTypes to “annotate” args with information used by addons that make use of those args. For instance, to instruct the [controls panel](../essentials/controls) to render a color picker, you could specify the `'color'` control type.
The most concrete realization of argTypes is the [`ArgTypes` doc block](./doc-blocks/doc-block-argtypes) ([`Controls`](./doc-blocks/doc-block-controls) is similar). Each row in the table corresponds to a single argType and the current value of that arg.

##
Automatic argType inference
If you are using the Storybook [docs](../writing-docs) addon, then Storybook will infer a set of argTypes for each story based on the `component` specified in the [default export](../writing-stories/index#default-export) of the CSF file.
To do so, Storybook uses various static analysis tools depending on your framework.
Framework| Static analysis tool
---|---
React| [react-docgen](https://github.com/reactjs/react-docgen) (default) or [react-docgen-typescript](https://github.com/styleguidist/react-docgen-typescript)
Vue| [vue-docgen-api](https://github.com/vue-styleguidist/vue-styleguidist/tree/dev/packages/vue-docgen-api)
Angular| [compodoc](https://compodoc.app/)
WebComponents| [custom-element.json](https://github.com/webcomponents/custom-elements-json)
Ember| [YUI doc](https://github.com/ember-learn/ember-cli-addon-docs-yuidoc#documenting-components)
The data structure of `argTypes` is designed to match the output of the these tools. Properties specified manually will override what is inferred.
##
Manually specifying argTypes
For most Storybook projects, argTypes are automatically inferred from your components. Any argTypes specified manually will override the inferred values.
ArgTypes are most often specified at the meta (component) level, in the [default export](../writing-stories/index#default-export) of the CSF file:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
argTypes: {
// 👇 All Button stories expect a label arg
label: {
control: 'text',
description: 'Overwritten description',
},
},
} satisfies Meta;
export default meta;
They can apply to all stories when specified at the project (global) level, in the `preview.js|ts` configuration file:
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview = {
argTypes: {
// 👇 All stories expect a label arg
label: {
control: 'text',
description: 'Overwritten description',
},
},
} satisfies Preview;
export default preview;
Or they can apply only to a [specific story](../writing-stories/index#defining-stories):
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Basic: Story = {
argTypes: {
// 👇 This story expects a label arg
label: {
control: 'text',
description: 'Overwritten description',
},
},
} satisfies Story;
##
`argTypes`
Type:
{
[key: string]: {
control?: ControlType | { type: ControlType; /* See below for more */ } | false;
description?: string;
if?: Conditional;
mapping?: { [key: string]: { [option: string]: any } };
name?: string;
options?: string[];
table?: {
category?: string;
defaultValue?: { summary: string; detail?: string };
disable?: boolean;
subcategory?: string;
type?: { summary?: string; detail?: string };
},
type?: SBType | SBScalarType['name'];
}
}
You configure argTypes using an object with keys matching the name of args. The value of each key is an object with the following properties:
###
`control`
Type:
| ControlType
| {
type: ControlType,
accept?: string;
labels?: { [option: string]: string };
max?: number;
min?: number;
presetColors?: string[];
step?: number;
}
| false
Default:
1. `'select'`, if `options` are specified
2. Else, inferred from `type`
3. Else, `'object'`
Specify the behavior of the [controls panel](../essentials/controls) for the arg. If you specify a string, it's used as the `type` of the control. If you specify an object, you can provide additional configuration. Specifying `false` will prevent the control from rendering.
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
value: {
control: {
type: 'number',
min: 0,
max: 100,
step: 10,
},
},
},
} satisfies Meta;
export default meta;
####
`control.type`
Type: `ControlType | null`
Default: Inferred; `'select'`, if `options` are specified; falling back to `'object'`
Specifies the type of control used to change the arg value with the [controls panel](../essentials/controls). Here are the available types, `ControlType`, grouped by the type of data they handle:
Data type| ControlType| Description
---|---|---
**array**| `'object'`| Provides a JSON-based editor to handle the values of the array. Also allows editing in raw mode.
`{ control: 'object' }`
**boolean**| `'boolean'`| Provides a toggle for switching between possible states.
`{ control: 'boolean' }`
**enum**| `'check'`| Provides a set of stacked checkboxes for selecting multiple options.
`{ control: 'check', options: ['email', 'phone', 'mail'] }`
| `'inline-check'`| Provides a set of inlined checkboxes for selecting multiple options.
`{ control: 'inline-check', options: ['email', 'phone', 'mail'] }`
| `'radio'`| Provides a set of stacked radio buttons based on the available options.
`{ control: 'radio', options: ['email', 'phone', 'mail'] }`
| `'inline-radio'`| Provides a set of inlined radio buttons based on the available options.
`{ control: 'inline-radio', options: ['email', 'phone', 'mail'] }`
| `'select'`| Provides a select to choose a single value from the options.
`{ control: 'select', options: [20, 30, 40, 50] }`
| `'multi-select'`| Provides a select to choose multiple values from the options.
`{ control: 'multi-select', options: ['USA', 'Canada', 'Mexico'] }`
**number**| `'number'`| Provides a numeric input to include the range of all possible values.
`{ control: { type: 'number', min:1, max:30, step: 2 } }`
| `'range'`| Provides a range slider to include all possible values.
`{ control: { type: 'range', min: 1, max: 30, step: 3 } }`
**object**| `'file'`| Provides a file input that returns an array of URLs. Can be further customized to accept specific file types.
`{ control: { type: 'file', accept: '.png' } }`
| `'object'`| Provides a JSON-based editor to handle the object's values. Also allows editing in raw mode.
`{ control: 'object' }`
**string**| `'color'`| Provides a color picker to choose color values. Can be additionally configured to include a set of color presets.
`{ control: { type: 'color', presetColors: ['red', 'green']} }`
| `'date'`| Provides a datepicker to choose a date.
`{ control: 'date' }`
| `'text'`| Provides a freeform text input.
`{ control: 'text' }`
💡
The `date` control will convert the date into a UNIX timestamp when the value changes. It's a known limitation that will be fixed in a future release. If you need to represent the actual date, you'll need to update the story's implementation and convert the value into a date object.
####
`control.accept`
Type: `string`
When `type` is `'file'`, you can specify the file types that are accepted. The value should be a string of comma-separated MIME types.
####
`control.labels`
Type: `{ [option: string]: string }`
Map `options` to labels. `labels` doesn't have to be exhaustive. If an option is not in the object's keys, it's used verbatim.
####
`control.max`
Type: `number`
When `type` is `'number'` or `'range'`, sets the maximum allowed value.
####
`control.min`
Type: `number`
When `type` is `'number'` or `'range'`, sets the minimum allowed value.
####
`control.presetColors`
Type: `string[]`
When `type` is `'color'`, defines the set of colors that are available in addition to the general color picker. The values in the array should be valid CSS color values.
####
`control.step`
Type: `number`
When `type` is `'number'` or `'range'`, sets the granularity allowed when incrementing/decrementing the value.
###
`description`
Type: `string`
Default: Inferred
Describe the arg. (If you intend to describe the type of the arg, you should use `table.type`, instead.)
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
value: {
description: 'The value of the slider',
},
},
} satisfies Meta;
export default meta;
###
`if`
Type:
{
[predicateType: 'arg' | 'global']: string;
eq?: any;
exists?: boolean;
neq?: any;
truthy?: boolean;
}
Conditionally render an argType based on the value of another [arg](../writing-stories/args) or [global](../essentials/toolbars-and-globals).
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
parent: { control: 'select', options: ['one', 'two', 'three'] },
// 👇 Only shown when `parent` arg exists
parentExists: { if: { arg: 'parent', exists: true } },
// 👇 Only shown when `parent` arg does not exist
parentDoesNotExist: { if: { arg: 'parent', exists: false } },
// 👇 Only shown when `parent` arg value is truthy
parentIsTruthy: { if: { arg: 'parent' } },
parentIsTruthyVerbose: { if: { arg: 'parent', truthy: true } },
// 👇 Only shown when `parent` arg value is not truthy
parentIsNotTruthy: { if: { arg: 'parent', truthy: false } },
// 👇 Only shown when `parent` arg value is 'three'
parentIsEqToValue: { if: { arg: 'parent', eq: 'three' } },
// 👇 Only shown when `parent` arg value is not 'three'
parentIsNotEqToValue: { if: { arg: 'parent', neq: 'three' } },
// Each of the above can also be conditional on the value of a globalType, e.g.:
// 👇 Only shown when `theme` global exists
parentExists: { if: { global: 'theme', exists: true } },
},
} satisfies Meta;
export default meta;
###
`mapping`
Type: `{ [key: string]: { [option: string]: any } }`
Map `options` to values.
When dealing with non-primitive values, you'll notice that you'll run into some limitations. The most obvious issue is that not every value can be represented as part of the `args` param in the URL, losing the ability to share and deeplink to such a state. Beyond that, complex values such as JSX cannot be synchronized between the manager (e.g., Controls panel) and the preview (your story).
`mapping` doesn't have to be exhaustive. If the currently selected option is not listed, it's used verbatim. Can be used with `control.labels`.
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
label: {
control: { type: 'select' },
options: ['Normal', 'Bold', 'Italic'],
mapping: {
Bold: Bold,
Italic: Italic,
},
},
},
} satisfies Meta;
export default meta;
###
`name`
Type: `string`
The `argTypes` object uses the name of the arg as the key. By default, that key is used when displaying the argType in Storybook. You can override the displayed name by specifying a `name` property.
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
actualArgName: {
name: 'Friendly name',
},
},
} satisfies Meta;
export default meta;
⚠️
Be careful renaming args in this way. Users of the component you're documenting will not be able to use the documented name as a property of your component and the actual name will not displayed.
For this reason, the `name` property is best used when defining an `argType` that is only used for documentation purposes and not an actual property of the component. For example, when [providing argTypes for each property of an object](https://stackblitz.com/edit/github-uplqzp?file=src/stories/Button.stories.tsx).
###
`options`
Type: `string[]`
Default: Inferred
If the arg accepts a finite set of values, you can specify them with `options`. If those values are [complex](../essentials/controls#dealing-with-complex-values), like JSX elements, you can use `mapping` to map them to string values. You can use `control.labels` to provide custom labels for the options.
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
icon: {
options: ['arrow-up', 'arrow-down', 'loading'],
},
},
} satisfies Meta;
export default meta;
###
`table`
Type:
{
category?: string;
defaultValue?: {
detail?: string;
summary: string;
};
disable?: boolean;
subcategory?: string;
type?: {
detail?: string;
summary: string;
};
}
Default: Inferred
Specify how the arg is documented in the [`ArgTypes` doc block](./doc-blocks/doc-block-argtypes), [`Controls` doc block](./doc-blocks/doc-block-controls), and [Controls panel](../essentials/controls).
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
value: {
table: {
defaultValue: { summary: 0 },
type: { summary: 'number' },
},
},
},
} satisfies Meta;
export default meta;
####
`table.category`
Type: `string`
Default: Inferred, in some frameworks
Display the argType under a category heading, with the label specified by `category`.
####
`table.defaultValue`
Type: `{ detail?: string; summary: string }`
Default: Inferred
The documented default value of the argType. `summary` is typically used for the value itself, while `detail` is used for additional information.
####
`table.disable`
Type: `boolean`
Set to `true` to remove the argType's row from the table.
####
`table.readonly`
Type: `boolean`
Set to `true` to indicate that the argType is read-only.
####
`table.subcategory`
Type: `string`
Display the argType under a subcategory heading (which displays under the [`category`] heading), with the label specified by `subcategory`.
####
`table.type`
Type: `{ detail?: string; summary: string }`
Default: Inferred from `type`
The documented type of the argType. `summary` is typically used for the type itself, while `detail` is used for additional information.
If you need to specify the actual, semantic type, you should use `type`, instead.
###
`type`
Type: `'boolean' | 'function' | 'number' | 'string' | 'symbol' | SBType`
The full type of `SBType` is:
SBType
interface SBBaseType {
required?: boolean;
raw?: string;
}
type SBScalarType = SBBaseType & {
name: 'boolean' | 'string' | 'number' | 'function' | 'symbol';
};
type SBArrayType = SBBaseType & {
name: 'array';
value: SBType;
};
type SBObjectType = SBBaseType & {
name: 'object';
value: Record;
};
type SBEnumType = SBBaseType & {
name: 'enum';
value: (string | number)[];
};
type SBIntersectionType = SBBaseType & {
name: 'intersection';
value: SBType[];
};
type SBUnionType = SBBaseType & {
name: 'union';
value: SBType[];
};
type SBOtherType = SBBaseType & {
name: 'other';
value: string;
};
type SBType =
| SBScalarType
| SBEnumType
| SBArrayType
| SBObjectType
| SBIntersectionType
| SBUnionType
| SBOtherType;
Default: Inferred
Specifies the semantic type of the argType. When an argType is inferred, the information from the various tools is summarized in this property, which is then used to infer other properties, like `control` and `table.type`.
If you only need to specify the documented type, you should use `table.type`, instead.
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
value: { type: 'number' },
},
} satisfies Meta;
export default meta;
###
`defaultValue`
(⛔️ **Deprecated**)
Type: `any`
Define the default value of the argType. Deprecated in favor of defining the [`arg`](../writing-stories/args) value directly.
CSF 3CSF Next 🧪
Example.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Example } from './Example';
const meta = {
component: Example,
argTypes: {
value: {
// ❌ Deprecated
defaultValue: 0,
},
},
// ✅ Do this instead
args: {
value: 0,
},
} satisfies Meta;
export default meta;
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/api/arg-types.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/api/cli-options
# Page: /docs/api/cli-options
# CLI options
The Storybook command line interface (CLI) is the main tool you use to build and develop Storybook.
ℹ️
Storybook collects completely anonymous data to help us improve user experience. Participation is optional, and you may [opt-out](../configure/telemetry#how-to-opt-out) if you'd not like to share any information.
##
CLI commands
All of the following documentation is available in the CLI by running `storybook --help`.
💡
Passing options to these commands works slightly differently if you're using npm instead of Yarn. You must prefix all of your options with `--`. For example, `npm run storybook build -- -o ./path/to/build --quiet`.
###
`dev`
Compiles and serves a development build of your Storybook that reflects your source code changes in the browser in real-time. It should be run from the root of your project.
storybook dev [options]
Options include:
Option| Description
---|---
`--help`| Output usage information.
`storybook dev --help`
`-V`, `--version`| Output the version number.
`storybook dev -V`
`-p`, `--port [number]`| Port to run Storybook.
`storybook dev -p 9009`
`--exact-port`| Attempts to run Storybook on the exact port number specified.
If the port is already in use, Storybook will exit with an error message.
`storybook dev -p 9009 --exact-port`
`-h`, `--host [string]`| Host to run Storybook.
`storybook dev -h my-host.com`
`-c`, `--config-dir [dir-name]`| Storybook configuration directory.
`storybook dev -c .storybook`
`--loglevel [level]`| Controls level of logging during build.
Available options: `silly`, `verbose`, `info` (default), `warn`, `error`, `silent`
`storybook dev --loglevel warn`
`--https`| Serve Storybook over HTTPS. Note: You must provide your own certificate information.
`storybook dev --https`
`--ssl-ca`| Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)
`storybook dev --ssl-ca my-certificate`
`--ssl-cert`| Provide an SSL certificate. (Required with --https)
`storybook dev --ssl-cert my-ssl-certificate`
`--ssl-key`| Provide an SSL key. (Required with --https)
`storybook dev --ssl-key my-ssl-key`
`--smoke-test`| Exit after successful start.
`storybook dev --smoke-test`
`--ci`| CI mode (skip interactive prompts, don't open browser).
`storybook dev --ci`
`--no-open`| Do not open Storybook automatically in the browser.
`storybook dev --no-open`
`--quiet`| Suppress verbose build output.
`storybook dev --quiet`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook dev --debug`
`--debug-webpack`| Display final webpack configurations for debugging purposes.
`storybook dev --debug-webpack`
`--stats-json [dir-name]`| Write stats JSON to disk.
Requires Webpack
`storybook dev --stats-json /tmp/stats`
`--no-version-updates`| Skips Storybook's update check.
`storybook dev --no-version-updates`
`--docs`| Starts Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation#preview-storybooks-documentation).
`storybook dev --docs`
`--initial-path [path]`| Configures the URL Storybook should open when it opens the browser for the first time.
`storybook dev --initial-path=/docs/getting-started--docs`
`--preview-url [path]`| Overrides the default Storybook preview with a custom built preview URL.
`storybook dev --preview-url=http://localhost:1337/external-iframe.html`
`--force-build-preview`| Forcefully builds Storybook's preview iframe.
Useful if you're experiencing issues, or combined with `--preview-url` to ensure the preview is up-to-date.
`storybook dev --force-build-preview`
`--disable-telemetry`| Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry#how-to-opt-out).
`storybook dev --disable-telemetry`
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry#crash-reports-disabled-by-default).
`storybook dev --enable-crash-reports`
`--preview-only`| Skips Storybook's manager from building and opens the app in "preview only" mode, which is designed to be used in [unsupported browsers](../sharing/publish-storybook#build-storybook-for-older-browsers).
`storybook dev --preview-only`
⚠️
With the release of Storybook 8, the `-s` CLI flag was removed. We recommend using the [static directory](../configure/integration/images-and-assets#serving-static-files-via-storybook) instead if you need to serve static files.
###
`build`
Compiles your Storybook instance so it can be [deployed](../sharing/publish-storybook). It should be run from the root of your project.
storybook build [options]
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook build --help`
`-V`, `--version`| Output the version number.
`storybook build -V`
`-o`, `--output-dir [dir-name]`| Directory where to store built files.
`storybook build -o /my-deployed-storybook`
`-c`, `--config-dir [dir-name]`| Storybook configuration directory.
`storybook build -c .storybook`
`--loglevel [level]`| Controls level of logging during build.
Available options: `silly`, `verbose`, `info` (default), `warn`, `error`, `silent`.
`storybook build --loglevel warn`
`--quiet`| Suppress verbose build output.
`storybook build --quiet`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook build --debug`
`--debug-webpack`| Display final webpack configurations for debugging purposes.
`storybook build --debug-webpack`
`--stats-json [dir-name]`| Write stats JSON to disk.
Requires Webpack
`storybook build --stats-json /tmp/stats`
`--docs`| Builds Storybook in documentation mode. Learn more about it in [here](../writing-docs/build-documentation#publish-storybooks-documentation).
`storybook build --docs`
`--test`| Optimize Storybook's production build for performance and tests by removing unnecessary features with the `test` option. Learn more [here](../api/main-config/main-config-build).
`storybook build --test`
`--preview-url [path]`| Overrides the default Storybook preview with a custom built preview URL.
`storybook build --preview-url=http://localhost:1337/external-iframe.html`
`--force-build-preview`| Forcefully builds Storybook's preview iframe.
Useful if you're experiencing issues, or combined with `--preview-url` to ensure the preview is up-to-date.
`storybook build --force-build-preview`
`--disable-telemetry`| Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry#how-to-opt-out).
`storybook build --disable-telemetry`
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry#crash-reports-disabled-by-default).
`storybook build --enable-crash-reports`
`--preview-only`| Skips Storybook's manager from building and produces a "preview only" app, which is designed to be used in [unsupported browsers](../sharing/publish-storybook#build-storybook-for-older-browsers).
`storybook build --preview-only`
###
`init`
ℹ️
We recommend `create-storybook` for new projects. The `init` command will remain available for backwards compatibility.
Installs and initializes the specified version (e.g., `@latest`, `@8`, `@next`) of Storybook into your project. If no version is specified, the latest version is installed. Read more in the [installation guide](../get-started/install).
storybook[@version] init [options]
For example, `storybook@8.4 init` will install Storybook 8.4 into your project.
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook init --help`
`-b`, `--builder`| Defines the [builder](../builders) to use for your Storybook instance.
`storybook init --builder webpack5`
`-f`, `--force`| Forcefully installs Storybook into your project, prompting you to overwrite existing files.
`storybook init --force`
`-s`, `--skip-install`| Skips the dependency installation step. Used only when you need to configure Storybook manually.
`storybook init --skip-install`
`-t`, `--type`| Defines the [framework](../configure/integration/frameworks) to use for your Storybook instance.
`storybook init --type solid`
`-y`, `--yes`| Skips interactive prompts and automatically installs Storybook per specified version, including all features.
`storybook init --yes`
`--features [...values]`| Use these features when installing, skipping the prompt. Supported values are `docs`, `test`, and `a11y`, space separated.
`storybook init --features docs test a11y`
`--package-manager`| Sets the package manager to use when installing Storybook.
Available package managers include `npm`, `yarn`, and `pnpm`.
`storybook init --package-manager pnpm`
`--use-pnp`| Enables [Plug'n'Play](https://yarnpkg.com/features/pnp) support for Yarn. This option is only available when using Yarn as your package manager.
`storybook init --use-pnp`
`-p`, `--parser`| Sets the [jscodeshift parser](https://github.com/facebook/jscodeshift#parser).
Available parsers include `babel`, `babylon`, `flow`, `ts`, and `tsx`.
`storybook init --parser tsx`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook init --debug`
`--disable-telemetry`| Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry#how-to-opt-out).
`storybook init --disable-telemetry`
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry#crash-reports-disabled-by-default).
`storybook init --enable-crash-reports`
`--loglevel `| Controls level of logging during initialization.
Available options: `trace`, `debug`, `info` (default), `warn`, `error`, `silent`
`storybook init --loglevel debug`
`--logfile [path]`| Write all debug logs to the specified file at the end of the run. Defaults to debug-storybook.log when [path] is not provided.
`storybook init --logfile /tmp/debug-storybook.log`
`--no-dev`| Complete the initialization of Storybook without running the Storybook dev server.
`storybook init --no-dev`
###
`add`
Installs a Storybook addon and configures your project for it. Read more in the [addon installation guide](../addons/install-addons).
storybook add [addon] [options]
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook add --help`
`-c`, `--config-dir`| Storybook configuration directory.
`storybook migrate --config-dir .storybook`
`--package-manager`| Sets the package manager to use when installing the addon.
Available package managers include `npm`, `yarn`, and `pnpm`.
`storybook add [addon] --package-manager pnpm`
`-s`, `--skip-postinstall`| Skips post-install configuration. Used only when you need to configure the addon yourself.
`storybook add [addon] --skip-postinstall`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook add --debug`
`--loglevel `| Controls level of logging during addon installation.
Available options: `trace`, `debug`, `info` (default), `warn`, `error`, `silent`
`storybook add [addon] --loglevel debug`
`--logfile [path]`| Write all debug logs to the specified file at the end of the run. Defaults to debug-storybook.log when [path] is not provided.
`storybook add [addon] --logfile /tmp/debug-storybook.log`
###
`remove`
Deletes a Storybook addon from your project. Read more in the [addon installation guide](../addons/install-addons#removing-addons).
storybook remove [addon] [options]
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook remove --help`
`--package-manager`| Sets the package manager to use when removing the addon.
Available package managers include `npm`, `yarn`, and `pnpm`.
`storybook remove [addon]--package-manager pnpm`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook remove --debug`
`--disable-telemetry`| Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry#how-to-opt-out).
`storybook remove --disable-telemetry`
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry#crash-reports-disabled-by-default).
`storybook remove --enable-crash-reports`
###
`upgrade`
Upgrades your Storybook instance to the specified version (e.g., `@latest`, `@8`, `@next`). Read more in the [upgrade guide](../releases/upgrading).
storybook[@version] upgrade [options]
For example, `storybook@latest upgrade --dry-run` will perform a dry run (no actual changes) of upgrading your project to the latest version of Storybook.
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook upgrade --help`
`-c, --config-dir `| Directory or directories to find Storybook configurations
`storybook upgrade --config-dir .storybook`
`-n`, `--dry-run`| Checks for version upgrades without installing them.
`storybook upgrade --dry-run`
`-s`, `--skip-check`| Skips the migration check step during the upgrade process.
`storybook upgrade --skip-check`
`-y`, `--yes`| Skips interactive prompts and automatically upgrades Storybook to the latest version.
`storybook upgrade --yes`
`-f`,`--force`| Force the upgrade, skipping autoblockers check.
`storybook upgrade --force`
`--package-manager`| Sets the package manager to use when upgrading Storybook.
Available package managers include `npm`, `yarn`, and `pnpm`.
`storybook upgrade --package-manager pnpm`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook upgrade --debug`
`--disable-telemetry`| Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry#how-to-opt-out).
`storybook upgrade --disable-telemetry`
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry#crash-reports-disabled-by-default).
`storybook upgrade --enable-crash-reports`
`-logfile [path]`| Write all debug logs to the specified file at the end of the run. Defaults to debug-storybook.log when [path] is not provided.
`storybook upgrade --logfile /tmp/debug-storybook.log`
`--loglevel `| Define log level: `debug`, `error`, `info`, `silent`, `trace`, or `warn` (default: `info`).
`storybook upgrade --loglevel debug`
###
`migrate`
Runs the provided codemod to ensure your Storybook project is compatible with the specified version. Read more in the [migration guide](../releases/upgrading).
storybook[@version] migrate [codemod] [options]
ℹ️
The command requires the codemod name (e.g., `csf-2-to-3`) as an argument to apply the necessary changes to your project. You can find the list of available codemods by running `storybook migrate --list`.
For example, `storybook@latest migrate csf-2-to-3 --dry-run`, checks your project to verify if the codemod can be applied without making any changes, providing you with a report of which files would be affected.
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook migrate --help`
`-c`, `--config-dir`| Storybook configuration directory.
`storybook migrate --config-dir .storybook`
`-n`, `--dry-run`| Verify the migration exists and show the files to which it will be applied.
`storybook migrate --dry-run`
`-l`, `--list`| Shows a list of available codemods.
`storybook migrate --list`
`-g`, `--glob`| Glob for files upon which to apply the codemods.
`storybook migrate --glob src/**/*.stories.tsx`
`-p`, `--parser`| Sets the [jscodeshift parser](https://github.com/facebook/jscodeshift#parser).
Available parsers include `babel`, `babylon`, `flow`, `ts`, and `tsx`.
`storybook migrate --parser tsx`
`-r`, `--rename [from-to]`| Renames the files affected by the codemod to include the provided suffix.
`storybook migrate --rename ".js:.ts"`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook migrate --debug`
###
`automigrate`
Perform standard configuration checks to determine if your Storybook project can be automatically migrated to the specified version. Read more in the [migration guide](../releases/upgrading#automigrate-script).
storybook[@version] automigrate [fixId] [options]
For example, `storybook@latest automigrate --dry-run` scans your project for potential migrations that can be applied automatically without making any changes.
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook automigrate --help`
`-c`, `--config-dir`| Storybook configuration directory.
`storybook automigrate --config-dir .storybook`
`-n`, `--dry-run`| Checks for available migrations without applying them.
`storybook automigrate --dry-run`
`-s`, `--skip-install`| Skip installing dependencies whenever applicable.
`storybook automigrate --skip-install`
`-y`, `--yes`| Applies available migrations automatically without prompting for confirmation.
`storybook automigrate --yes`
`-l`, `--list`| Shows a list of available automigrations.
`storybook automigrate --list`
`--package-manager`| Sets the package manager to use when running the auto migration.
Available package managers include `npm`, `yarn`, and `pnpm`.
`storybook automigrate --package-manager pnpm`
`--renderer`| Specifies Storybook's renderer to use when running the automigration.
Useful for monorepo environments where multiple Storybook instances can exist in the same project.
`storybook automigrate --renderer vue`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook automigrate --debug`
`--disable-telemetry`| Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry#how-to-opt-out).
`storybook automigrate --disable-telemetry`
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry#crash-reports-disabled-by-default).
`storybook automigrate --enable-crash-reports`
###
`doctor`
Performs a health check on your Storybook project for common issues (e.g., duplicate dependencies, incompatible addons or mismatched versions) and provides suggestions on how to fix them. Applicable when [upgrading](../releases/upgrading#verifying-the-upgrade) Storybook versions.
storybook doctor [options]
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook doctor --help`
`-c`, `--config-dir`| Storybook configuration directory.
`storybook doctor --config-dir .storybook`
`--package-manager`| Sets the package manager to use when running the health check.
Available package managers include `npm`, `yarn`, and `pnpm`.
`storybook doctor --package-manager pnpm`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook doctor --debug`
###
`info`
Reports useful debugging information about your environment. Helpful in providing information when opening an issue or a discussion.
storybook info
Example output:
Storybook Environment Info:
System:
OS: macOS 14.2
CPU: (8) arm64 Apple M3
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node
npm: 10.2.3 - ~/.nvm/versions/node/v18.19.0/bin/npm <----- active
Browsers:
Chrome: 120.0.6099.199
npmPackages:
@storybook/addon-onboarding: ^1.0.10 => 1.0.10
@storybook/react: ^7.6.6 => 7.6.6
@storybook/react-vite: ^7.6.6 => 7.6.6
storybook: ^7.6.6 => 7.6.6
npmGlobalPackages:
chromatic: ^10.2.0 => 10.2.0
###
`index`
Build an `index.json` that lists all stories and docs entries in your Storybook.
storybook index [options]
Options include:
Option| Description
---|---
`-o`, `--output-file `| JSON file to output index
`-c`, `--config-dir `| Storybook configuration directory
`--quiet`| Suppress verbose build output
`--loglevel `| Control level of logging during build
`--disable-telemetry`| Disables Storybook's telemetry
`--debug`| Outputs more logs in the CLI to assist debugging.
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry
###
`sandbox`
Generates a local sandbox project using the specified version (e.g., `@latest`, `@8`, `@next`) for testing Storybook features based on the list of supported [frameworks](../configure/integration/frameworks). Useful for reproducing bugs when opening an issue or a discussion.
storybook[@version] sandbox [framework-filter] [options]
For example, `storybook@next sandbox` will generated sandboxes using the newest pre-release version of Storybook.
The `framework-filter` argument is optional and can filter the list of available frameworks. For example, `storybook@next sandbox react` will only offer to generate React-based sandboxes.
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`storybook sandbox --help`
`-o`, `--output [dir-name]`| Configures the location of the sandbox project.
`storybook sandbox --output /my-sandbox-project`
`--no-init`| Generates a sandbox project without initializing Storybook.
`storybook sandbox --no-init`
`--debug`| Outputs more logs in the CLI to assist debugging.
`storybook sandbox --debug`
`--disable-telemetry`| Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry#how-to-opt-out).
`storybook sandbox --disable-telemetry`
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry#crash-reports-disabled-by-default).
`storybook sandbox --enable-crash-reports`
ℹ️
If you're looking for a hosted version of the available sandboxes, see [storybook.new](https://storybook.new).
##
`create-storybook`
To streamline the process of creating a new Storybook project, a separate CLI called `create-storybook` is provided. Package managers such as npm, pnpm, and Yarn will execute this command when running `create storybook`. You can specify a version (e.g., `@latest`, `@8`, `@next`) or it will default to the latest version. Read more in the [installation guide](../get-started/install).
create storybook[@version] [options]
For example, `create storybook@8.6` will install Storybook 8.6 into your project.
Options include:
Option| Description
---|---
`-h`, `--help`| Output usage information.
`create storybook --help`
`-b`, `--builder`| Defines the [builder](../builders) to use for your Storybook instance.
`create storybook --builder webpack5`
`-f`, `--force`| Forcefully installs Storybook into your project, prompting you to overwrite existing files.
`create storybook --force`
`-s`, `--skip-install`| Skips the dependency installation step. Used only when you need to configure Storybook manually.
`create storybook --skip-install`
`-t`, `--type`| Defines the [framework](../configure/integration/frameworks) to use for your Storybook instance.
`create storybook --type solid`
`-y`, `--yes`| Skips interactive prompts and automatically installs Storybook per specified version, including all features.
`create storybook --yes`
`--features [...values]`| Use these features when installing, skipping the prompt. Supported values are `docs`, `test`, and `a11y`, space separated.
`create storybook --features docs test a11y`
`--package-manager`| Sets the package manager to use when installing Storybook.
Available package managers include `npm`, `yarn`, and `pnpm`.
`create storybook --package-manager pnpm`
`--use-pnp`| Enables [Plug'n'Play](https://yarnpkg.com/features/pnp) support for Yarn. This option is only available when using Yarn as your package manager.
`create storybook --use-pnp`
`-p`, `--parser`| Sets the [jscodeshift parser](https://github.com/facebook/jscodeshift#parser).
Available parsers include `babel`, `babylon`, `flow`, `ts`, and `tsx`.
`create storybook --parser tsx`
`--debug`| Outputs more logs in the CLI to assist debugging.
`create storybook --debug`
`--disable-telemetry`| Disables Storybook's telemetry. Learn more about it [here](../configure/telemetry#how-to-opt-out).
`create storybook --disable-telemetry`
`--enable-crash-reports`| Enables sending crash reports to Storybook's telemetry. Learn more about it [here](../configure/telemetry#crash-reports-disabled-by-default).
`create storybook --enable-crash-reports`
`--loglevel `| Controls level of logging during initialization.
Available options: `trace`, `debug`, `info` (default), `warn`, `error`, `silent`
`storybook init --loglevel debug`
`--logfile [path]`| Write all debug logs to the specified file at the end of the run. Defaults to debug-storybook.log when [path] is not provided.
`storybook init --logfile /tmp/debug-storybook.log`
`--no-dev`| Complete the initialization of Storybook without running the Storybook dev server.
`create storybook --no-dev`
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/api/cli-options.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/api/csf
# Page: /docs/api/csf
# Component Story Format (CSF)
ReactVueAngularWeb ComponentsMore
CSF 3[CSF Next (Preview)](./csf/csf-next)
Component Story Format (CSF) is the recommended way to [write stories](./../writing-stories). It's an open standard based on ES6 modules that is portable beyond Storybook.
In CSF, stories and component metadata are defined as ES Modules. Every component story file consists of a required [default export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Using_the_default_export) and one or more [named exports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export).
##
Default export
The default export defines metadata about your component, including the `component` itself, its `title` (where it will show up in the [navigation UI story hierarchy](./../writing-stories/naming-components-and-hierarchy#sorting-stories)), [decorators](./../writing-stories/decorators), and [parameters](./../writing-stories/parameters).
The `component` field is required and used by addons for automatic prop table generation and display of other component metadata. The `title` field is optional and should be unique (i.e., not re-used across files).
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta = {
/* 👇 The title prop is optional.
* See https://storybook.js.org/docs/configure/#configure-story-loading
* to learn how to generate automatic titles
*/
title: 'Path/To/MyComponent',
component: MyComponent,
decorators: [
/* ... */
],
parameters: {
/* ... */
},
} satisfies Meta;
export default meta;
For more examples, see [writing stories](./../writing-stories).
##
Named story exports
With CSF, every named export in the file represents a story object by default.
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Basic: Story = {};
export const WithProp: Story = {
render: () => ,
};
The exported identifiers will be converted to "start case" using Lodash's [startCase](https://lodash.com/docs/#startCase) function. For example:
Identifier| Transformation
---|---
name| Name
someName| Some Name
someNAME| Some NAME
some_custom_NAME| Some Custom NAME
someName1234| Some Name 1 2 3 4
We recommend that all export names to start with a capital letter.
Story objects can be annotated with a few different fields to define story-level [decorators](./../writing-stories/decorators) and [parameters](./../writing-stories/parameters), and also to define the `name` of the story.
Storybook's `name` configuration element is helpful in specific circumstances. Common use cases are names with special characters or Javascript restricted words. If not specified, Storybook defaults to the named export.
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Simple: Story = {
name: 'So simple!',
// ...
};
##
Args story inputs
Starting in SB 6.0, stories accept named inputs called Args. Args are dynamic data that are provided (and possibly updated by) Storybook and its addons.
Consider Storybook’s ["Button" example](./../writing-stories/index#defining-stories) of a text button that logs its click events:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { action } from 'storybook/actions';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Basic: Story = {
render: () => ,
};
Now consider the same example, re-written with args:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { action } from 'storybook/actions';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Text = {
args: {
label: 'Hello',
onClick: action('clicked'),
},
render: ({ label, onClick }) => ,
};
Or even more simply:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Text: Story = {
args: {},
};
Not only are these versions shorter and more accessible to write than their no-args counterparts, but they are also more portable since the code doesn't depend on the actions feature specifically.
For more information on setting up [Docs](./../writing-docs) and [Actions](./../essentials/actions), see their respective documentation.
##
Play function
Storybook's `play` functions are small snippets of code executed when the story renders in the UI. They are convenient helper methods to help you test use cases that otherwise weren't possible or required user intervention.
A good use case for the `play` function is a form component. With previous Storybook versions, you'd write your set of stories and had to interact with the component to validate it. With Storybook's play functions, you could write the following story:
CSF 3CSF Next 🧪
LoginForm.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { expect } from 'storybook/test';
import { LoginForm } from './LoginForm';
const meta = {
component: LoginForm,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const EmptyForm: Story = {};
export const FilledForm: Story = {
play: async ({ canvas, userEvent }) => {
// 👇 Simulate interactions with the component
await userEvent.type(canvas.getByTestId('email'), 'email@provider.com');
await userEvent.type(canvas.getByTestId('password'), 'a-random-password');
// See https://storybook.js.org/docs/essentials/actions#automatically-matching-args to learn how to setup logging in the Actions panel
await userEvent.click(canvas.getByRole('button'));
// 👇 Assert DOM structure
await expect(
canvas.getByText(
'Everything is perfect. Your account is ready and we should probably get you started!',
),
).toBeInTheDocument();
},
};
When the story renders in the UI, Storybook executes each step defined in the `play` function and runs the assertions without the need for user interaction.
##
Custom render functions
Starting in Storybook 6.4, you can write your stories as JavaScript objects, reducing the boilerplate code you need to generate to test your components, thus improving functionality and usability. `Render` functions are helpful methods to give you additional control over how the story renders. For example, if you were writing a story as an object and you wanted to specify how your component should render, you could write the following:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Layout } from './Layout';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
// This story uses a render function to fully control how the component renders.
export const Example: Story = {
render: () => (
Example
),
};
When Storybook loads this story, it will detect the existence of a `render` function and adjust the component rendering accordingly based on what's defined.
##
Storybook export vs. name handling
Storybook handles named exports and the `name` option slightly differently. When should you use one vs. the other?
Storybook will always use the named export to determine the story ID and URL.
If you specify the `name` option, it will be used as the story display name in the UI. Otherwise, it defaults to the named export, processed through Storybook's `storyNameFromExport` and `lodash.startCase` functions.
MyComponent-test.js
it('should format CSF exports with sensible defaults', () => {
const testCases = {
name: 'Name',
someName: 'Some Name',
someNAME: 'Some NAME',
some_custom_NAME: 'Some Custom NAME',
someName1234: 'Some Name 1234',
someName1_2_3_4: 'Some Name 1 2 3 4',
};
Object.entries(testCases).forEach(([key, val]) => {
expect(storyNameFromExport(key)).toBe(val);
});
});
When you want to change the name of your story, rename the CSF export. It will change the name of the story and also change the story's ID and URL.
It would be best if you used the `name` configuration element in the following cases:
1. You want the name to show up in the Storybook UI in a way that's not possible with a named export, e.g., reserved keywords like "default", special characters like emoji, spacing/capitalization other than what's provided by `storyNameFromExport`.
2. You want to preserve the Story ID independently from changing how it's displayed. Having stable Story IDs is helpful for integration with third-party tools.
##
Non-story exports
In some cases, you may want to export a mixture of stories and non-stories (e.g., mocked data).
You can use the optional configuration fields `includeStories` and `excludeStories` in the default export to make this possible. You can define them as an array of strings or regular expressions.
Consider the following story file:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
import someData from './data.json';
const meta = {
component: MyComponent,
includeStories: ['SimpleStory', 'ComplexStory'], // 👈 Storybook loads these stories
excludeStories: /.*Data$/, // 👈 Storybook ignores anything that contains Data
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const simpleData = { foo: 1, bar: 'baz' };
export const complexData = { foo: 1, foobar: { bar: 'baz', baz: someData } };
export const SimpleStory: Story = {
args: {
data: simpleData,
},
};
export const ComplexStory: Story = {
args: {
data: complexData,
},
};
When this file renders in Storybook, it treats `ComplexStory` and `SimpleStory` as stories and ignores the `data` named exports.
For this particular example, you could achieve the same result in different ways, depending on what's convenient:
* `includeStories: /^[A-Z]/`
* `includeStories: /.*Story$/`
* `includeStories: ['SimpleStory', 'ComplexStory']`
* `excludeStories: /^[a-z]/`
* `excludeStories: /.*Data$/`
* `excludeStories: ['simpleData', 'complexData']`
The first option is the recommended solution if you follow the best practice of starting story exports with an uppercase letter (i.e., use UpperCamelCase).
##
Upgrading from CSF 2 to CSF 3
ℹ️
Storybook provides a codemod to help you upgrade from CSF 2 to CSF 3. You can run it with the following command:
npm
npx storybook migrate csf-2-to-3 --glob="**/*.stories.tsx" --parser=tsx
In CSF 2, the named exports are always functions that instantiate a component, and those functions can be annotated with configuration options. For example:
CSF 2 - Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { ComponentStory, ComponentMeta } from '@storybook/your-framework';
import { Button } from './Button';
export default {
title: 'Button',
component: Button,
} as ComponentMeta;
export const Primary: ComponentStory = (args) => ;
Primary.args = { primary: true };
This declares a Primary story for a Button that renders itself by spreading `{ primary: true }` into the component. The `default.title` metadata says where to place the story in a navigation hierarchy.
Here's the CSF 3 equivalent:
CSF 3 - Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Primary: Story = { args: { primary: true } };
Let's go through the changes individually to understand what's going on.
###
Spreadable story objects
In CSF 3, the named exports are **objects** , not functions. This allows us to reuse stories more efficiently with the JS spread operator.
Consider the following addition to the intro example, which creates a `PrimaryOnDark` story that renders against a dark background:
Here's the CSF 2 implementation:
CSF 2 - Button.stories.js|jsx|ts|tsx
export const PrimaryOnDark = Primary.bind({});
PrimaryOnDark.args = Primary.args;
PrimaryOnDark.parameters = { background: { default: 'dark' } };
`Primary.bind({})` copies the story function, but it doesn't copy the annotations hanging off the function, so we must add `PrimaryOnDark.args = Primary.args` to inherit the args.
In CSF 3, we can spread the `Primary` object to carry over all its annotations:
CSF 3 - Button.stories.ts|tsx
Typescript
export const PrimaryOnDark: Story = {
...Primary,
parameters: { background: { default: 'dark' } },
};
Learn more about named story exports.
###
Default render functions
In CSF 3, you specify how a story renders through a `render` function. We can rewrite a CSF 2 example to CSF 3 through the following steps.
Let's start with a simple CSF 2 story function:
CSF 2 - Button.stories.ts|tsx
Typescript
// Other imports and story implementation
export const Default: ComponentStory = (args) => ;
Now, let's rewrite it as a story object in CSF 3 with an explicit `render` function that tells the story how to render itself. Like CSF 2, this gives us full control of how we render a component or even a collection of components.
CSF 3 - Button.stories.ts|tsx
Typescript
// Other imports and story implementation
export const Default: Story = {
render: (args) => ,
};
Learn more about render functions.
But in CSF 2, a lot of story functions are identical: take the component specified in the default export and spread args into it. What's interesting about these stories is not the function, but the args passed into the function.
CSF 3 provides default render functions for each renderer. If all you're doing is spreading args into your component—which is the most common case—you don't need to specify any `render` function at all:
CSF 3 - Button.stories.js|jsx|ts|tsx
export const Default = {};
For more information, see the section on custom render functions.
###
Generate titles automatically
Finally, CSF 3 can automatically generate titles.
CSF 2 - Button.stories.js|jsx|ts|tsx
export default {
title: 'components/Button',
component: Button,
};
CSF 3 - Button.stories.js|jsx|ts|tsx
export default { component: Button };
You can still specify a title like in CSF 2, but if you don't specify one, it can be inferred from the story's path on disk. For more information, see the section on [configuring story loading](./../configure/index#configure-story-loading).
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/api/csf/index.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/api/new-frameworks
# Page: /docs/api/new-frameworks
# Frameworks
ReactVueAngularWeb ComponentsMore
Storybook is architected to support diverse web frameworks, including React, Vue, Angular, Web Components, Svelte, and over a dozen others. This guide helps you get started on adding new framework support for Storybook.
##
Scaffolding a new framework
The first thing to do is to scaffold your framework support in its own repo.
We recommend adopting the same project structure as the Storybook monorepo. That structure contains the framework package (`app/`) and an example app (`examples/-kitchen-sink`) as well as other associated documentation and configuration as needed.
It may seem like a little more hierarchy than what’s necessary. But because the structure mirrors the way Storybook’s monorepo is structured, you can reuse Storybook’s tooling. It also makes it easier to move the framework into the Storybook monorepo later if that is desirable.
We recommend using `@storybook/html` as a starter framework since it’s the simplest and contains no framework-specific peculiarities. There is a boilerplate to get you started [here](https://github.com/CodeByAlex/storybook-framework-boilerplate).
##
Framework architecture
Supporting a new framework in Storybook typically consists of two main aspects:
1. Configuring the server. In Storybook, the server is the node process that runs when you run `storybook dev` or `storybook build`. Configuring the server typically means configuring babel and webpack in framework-specific ways.
2. Configuring the client. The client is the code that runs in the browser, and configuring it, means providing a framework-specific story rendering function.
##
Configuring the server
Storybook has the concept of [presets](../addons/writing-presets), which are typically babel/webpack configurations for file loading. If your framework has its own file format (e.g., “.vue”), you might need to transform them into JavaScript files at load time. If you assume every user of your framework needs this, you should add it to the framework. So far, every framework added to Storybook has done it because Storybook’s core configuration is extremely minimal.
###
Package structure
It's helpful to understand Storybook's package structure before adding a framework preset. Each framework typically exposes two executables in its `package.json`:
package.json
{
"bin": {
"storybook": "./bin/index.js",
"build-storybook": "./bin/build.js"
}
}
These scripts pass an `options` object to `storybook/internal/server`, a library that abstracts all of Storybook’s framework-independent code.
For example, here’s the boilerplate to start the dev server with `storybook dev`:
your-framework/src/server/index.ts
import { buildDev } from '@storybook/core/server';
import options from './options';
buildDev(options);
Thus the essence of adding framework presets is just filling in that options object.
###
Server options
As described above, the server `options` object does the heavy lifting of configuring the server.
Let’s look at the `@storybook/vue`’s options definition:
vue/src/server/options.ts
import { readFileSync } from 'node:fs';
import * as pkg from 'empathic/package';
export default {
packageJson: JSON.parse(readFileSync(pkg.up({ cwd: process.cwd() }))),
framework: 'vue',
frameworkPresets: [import.meta.resolve('./framework-preset-vue.js')],
};
The value of the `framework` option (i.e., ‘vue’) is something that gets passed to addons and allows them to do specific tasks related to your framework.
The essence of this file is the framework presets, and these are standard [Storybook presets](../addons/writing-presets) \-- you can look at framework packages in the Storybook monorepo (e.g. [React](https://github.com/storybookjs/storybook/blob/main/app/react/src/server/options.ts), [Vue](https://github.com/storybookjs/storybook/blob/main/app/vue/src/server/options.ts), [Web Components](https://github.com/storybookjs/storybook/blob/main/app/web-components/src/server/options.ts)) to see examples of framework-specific customizations.
While developing your custom framework, not maintained by Storybook, you can specify the path to the location file with the `frameworkPath` key:
my-framework/src/server/options.ts
import { readFileSync } from 'node:fs';
import * as pkg from 'empathic/package';
export default {
packageJson: JSON.parse(readFileSync(pkg.up({ cwd: process.cwd() }))),
framework: 'my-framework',
frameworkPath: '@my-framework/storybook',
frameworkPresets: [import.meta.resolve('./framework-preset-my-framework.js')],
};
You can add a relative path to `frameworkPath`. Don't forget that they resolve from the Storybook configuration directory (i.e., `.storybook`) by default.
Make sure the `frameworkPath` ends up at the `dist/client/index.js` file within your framework app.
##
Configuring the client
To configure the client, you must provide a framework-specific render function. Before diving into the details, it’s essential to understand how user-written stories relate to what renders on the screen.
###
Renderable objects
Storybook stories are ES6 objects that return a “renderable object.”
Consider the following React story:
Button.stories.js|jsx
import { Button } from './Button';
export default {
component: Button,
};
export const Sample = {
render: () => ,
};
In this case, the renderable object is the React element, ``.
In most other frameworks, the renderable object is actually a plain JavaScript object.
Consider the following hypothetical example:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Sample: Story = {
render: () => ({
template: '',
data: {
label: 'hello button',
},
}),
};
The design of this “renderable object” is framework-specific and should ideally match the idioms of that framework.
###
Render function
The framework's render function is the entity responsible for converting the renderable object into DOM nodes. It is typically of the form:
your-framework/src/client/preview/render.ts
const rootElement = document.getElementById('root');
export default function renderMain({ storyFn }: RenderMainArgs) {
const storyObj = storyFn();
const html = fn(storyObj);
rootElement.innerHTML = html;
}
###
Package structure
On the client side, the key file is [`src/client/preview.js`](../configure/index#configure-story-rendering):
your-framework/src/client/preview/index.ts
import { start } from 'storybook/preview-api';
import './globals';
import render from './render';
const api = start(render);
// the boilerplate code
The globals file typically sets up a single global variable that client-side code (such as addon-provided decorators) can refer to if needed to understand which framework it's running in:
vue/src/client/preview/globals.ts
import { global } from '@storybook/global';
const { window: globalWindow } = global;
globalWindow.STORYBOOK_ENV = 'vue';
The `start` function abstracts all of Storybook’s framework-independent client-side (browser) code, and it takes the render function we defined above. For examples of render functions, see [React](https://github.com/storybookjs/storybook/blob/main/app/react/src/client/preview/render.tsx), [Vue](https://github.com/storybookjs/storybook/blob/main/app/vue/src/client/preview/render.ts), [Angular](https://github.com/storybookjs/storybook/blob/main/app/angular/src/client/preview/render.ts), and [Web Components](https://github.com/storybookjs/storybook/blob/main/app/web-components/src/client/preview/render.ts) in the Storybook monorepo.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/api/new-frameworks.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/api/parameters
# Page: /docs/api/parameters
# Parameters
ReactVueAngularWeb ComponentsMore
Parameters are static metadata used to configure your [stories](../get-started/whats-a-story) and [addons](../addons) in Storybook. They are specified at the story, meta (component), project (global) levels.
##
Story parameters
Parameters specified at the story level apply to that story only. They are defined in the `parameters` property of the story (named export):
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Primary: Story = {
// 👇 Story-level parameters
parameters: {
backgrounds: {
options: {
red: { name: 'Red', value: '#f00' },
green: { name: 'Green', value: '#0f0' },
blue: { name: 'Blue', value: '#00f' },
},
},
},
};
ℹ️
Parameters specified at the story level will override those specified at the project level and meta (component) level.
##
Meta parameters
Parameter's specified in a [CSF](../writing-stories/index#component-story-format-csf) file's meta configuration apply to all stories in that file. They are defined in the `parameters` property of the `meta` (default export):
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
//👇 Creates specific parameters at the component level
parameters: {
backgrounds: {
options: {},
},
},
} satisfies Meta;
export default meta;
ℹ️
Parameters specified at the meta (component) level will override those specified at the project level.
##
Project parameters
Parameters specified at the project (global) level apply to **all stories** in your Storybook. They are defined in the `parameters` property of the default export in your `.storybook/preview.js|ts` file:
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
parameters: {
backgrounds: {
options: {
light: { name: 'Light', value: '#fff' },
dark: { name: 'Dark', value: '#333' },
},
},
},
};
export default preview;
##
Available parameters
Storybook only accepts a few parameters directly.
###
`layout`
Type: `'centered' | 'fullscreen' | 'padded'`
Default: `'padded'`
Specifies how the canvas should [lay out the story](../configure/story-layout).
* **centered** : Center the story within the canvas
* **padded** : (default) Add padding to the story
* **fullscreen** : Show the story as-is, without padding
###
`options`
Type:
{
storySort?: StorySortConfig | StorySortFn;
}
⚠️
The `options` parameter can _only_ be applied at the project level.
####
`options.storySort`
Type: `StorySortConfig | StorySortFn`
type StorySortConfig = {
includeNames?: boolean;
locales?: string;
method?: 'alphabetical' | 'alphabetical-by-kind' | 'custom';
order?: string[];
};
type Story = {
id: string;
importPath: string;
name: string;
title: string;
};
type StorySortFn = (a: Story, b: Story) => number;
Specifies the order in which stories are displayed in the Storybook UI.
When specifying a configuration object, the following options are available:
* **includeNames** : Whether to include the story name in the sorting algorithm. Defaults to `false`.
* **locales** : The locale to use when sorting stories. Defaults to your system locale.
* **method** : The sorting method to use. Defaults to `alphabetical`.
* **alphabetical** : Sort stories alphabetically by name.
* **alphabetical-by-kind** : Sort stories alphabetically by kind, then by name.
* **custom** : Use a custom sorting function.
* **order** : Stories in the specified order will be displayed first, in the order specified. All other stories will be displayed after, in alphabetical order. The order array can accept a nested array to sort 2nd-level story kinds, e.g. `['Intro', 'Pages', ['Home', 'Login', 'Admin'], 'Components']`.
When specifying a custom sorting function, the function behaves like a typical JavaScript sorting function. It accepts two stories to compare and returns a number. For example:
(a, b) => (a.id === b.id ? 0 : a.id.localeCompare(b.id, undefined, { numeric: true }));
See [the guide](../writing-stories/naming-components-and-hierarchy#sorting-stories) for usage examples.
###
`test`
Type:
{
clearMocks?: boolean;
mockReset?: boolean;
restoreMocks?: boolean;
dangerouslyIgnoreUnhandledErrors?: boolean;
}
####
`clearMocks`
Type: `boolean`
Default: `false`
[Similar to Vitest](https://vitest.dev/config/#clearmocks), it will call `.mockClear()` on all spies created with `fn()` from `storybook/test` when a story unmounts. This will clear mock history, but not reset its implementation to the default one.
####
`mockReset`
Type: `boolean`
Default: `false`
[Similar to Vitest](https://vitest.dev/config/#mockreset), it will call `.mockReset()` on all spies created with `fn()` from `storybook/test` when a story unmounts. This will clear mock history and reset its implementation to an empty function (will return `undefined`).
####
`restoreMocks`
Type: `boolean`
Default: `true`
[Similar to Vitest](https://vitest.dev/config/#restoremocks), it will call `.restoreMocks()` on all spies created with `fn()` from `storybook/test` when a story unmounts. This will clear mock history and reset its implementation to the original one.
####
`dangerouslyIgnoreUnhandledErrors`
Type: `boolean`
Default: `false`
Unhandled errors might cause false positive assertions. Setting this to `true` will prevent the [play function](../writing-stories/play-function) from failing and showing a warning when unhandled errors are thrown during execution.
* * *
###
Essentials
All other parameters are contributed by features. The [essential feature's](../essentials) parameters are documented on their individual pages:
* [Actions](../essentials/actions#parameters)
* [Backgrounds](../essentials/backgrounds#parameters)
* [Controls](../essentials/controls#parameters)
* [Highlight](../essentials/highlight#parameters)
* [Measure & Outline](../essentials/measure-and-outline#parameters)
* [Viewport](../essentials/viewport#parameters)
##
Parameter inheritance
No matter where they're specified, parameters are ultimately applied to a single story. Parameters specified at the project (global) level are applied to every story in that project. Those specified at the meta (component) level are applied to every story associated with that meta. And parameters specified for a story only apply to that story.
When specifying parameters, they are merged together in order of increasing specificity:
1. Project (global) parameters
2. Meta (component) parameters
3. Story parameters
ℹ️
Parameters are **merged** , so objects are deep-merged, but arrays and other properties are overwritten.
In other words, the following specifications of parameters:
.storybook/preview.js|ts
const preview = {
// 👇 Project-level parameters
parameters: {
layout: 'centered',
demo: {
demoProperty: 'a',
demoArray: [1, 2],
},
},
// ...
};
export default preview;
Dialog.stories.js|ts
const meta = {
component: Dialog,
// 👇 Meta-level parameters
parameters: {
layout: 'fullscreen',
demo: {
demoProperty: 'b',
anotherDemoProperty: 'b',
},
},
};
export default meta;
// (no additional parameters specified)
export const Basic = {};
export const LargeScreen = {
// 👇 Story-level parameters
parameters: {
layout: 'padded',
demo: {
demoArray: [3, 4],
},
},
};
Will result in the following parameter values applied to each story:
// Applied story parameters
// For the Basic story:
{
layout: 'fullscreen',
demo: {
demoProperty: 'b',
anotherDemoProperty: 'b',
demoArray: [1, 2],
},
}
// For the LargeScreen story:
{
layout: 'padded',
demo: {
demoProperty: 'b',
anotherDemoProperty: 'b',
demoArray: [3, 4],
},
}
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/api/parameters.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/api
# Page: /docs/api
# API references
An overview of all available API references for Storybook.
##
Configuration
Name| Description
---|---
[`main.js|ts`](./api/main-config/main-config)| Storybook's primary configuration file, which specifies your Storybook project's behavior, including the location of your stories, the addons you use, feature flags and other project-specific settings.
[`preview.js|jsx|ts|tsx`](./configure/#configure-story-rendering)| This configuration file controls the way stories are rendered. You can also use it to run code that applies to all stories.
[`manager.js|ts`](./configure/#configure-storybooks-ui)| This configuration file controls the behavior of Storybook's UI, the manager.
[CLI](./api/cli-options)| Storybook is a CLI tool. You can start Storybook in development mode or build a static version of your Storybook.
##
Stories
Name| Description
---|---
[CSF](./api/csf)| Component Story Format (CSF) is the API for writing stories. It's an open standard based on ES6 modules that is portable beyond Storybook.
[ArgTypes](./api/arg-types)| ArgTypes specify the behavior of [args](./writing-stories/args). By specifying the type of an arg, you constrain the values that it can accept and provide information about args that are not explicitly set.
[Parameters](./api/parameters)| Parameters are static metadata used to configure your [stories](./get-started/whats-a-story) [addons](./addons) in Storybook. They are specified at the story, meta (component), project (global) levels.
##
Docs
Name| Description
---|---
[Doc blocks](./writing-docs/doc-blocks/#available-blocks)| Storybook offers several doc blocks to help document your components and other aspects of your project.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/api/index.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/builders/builder-api
# Page: /docs/builders/builder-api
# Builder API
ReactVueAngularWeb ComponentsMore
Storybook is architected to support multiple builders, including [Webpack](https://webpack.js.org/), [Vite](https://vitejs.dev/), and [ESBuild](https://esbuild.github.io/). The builder API is the set of interfaces you can use to add a new builder to Storybook.

##
How do builders work?
In Storybook, a builder is responsible for compiling your components and stories into JS bundles that run in the browser. A builder also provides a development server for interactive development and a production mode for optimized bundles.
To opt into a builder, the user must add it as a dependency and then edit their configuration file (`.storybook/main.js`) to enable it. For example, with the Vite builder:
npm
npm install @storybook/builder-vite --save-dev
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite, angular, etc.)
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
builder: '@storybook/builder-vite', // 👈 The builder enabled here.
},
};
export default config;
##
Builder API
In Storybook, every builder must implement the following [API](https://github.com/storybookjs/storybook/blob/main/code/core/src/types/modules/core-common.ts#L239-L259), exposing the following configuration options and entry points:
export interface Builder {
start: (args: {
options: Options;
startTime: ReturnType;
router: Router;
server: Server;
}) => Promise;
bail: (e?: Error) => Promise;
}>;
build: (arg: {
options: Options;
startTime: ReturnType;
}) => Promise;
bail: (e?: Error) => Promise;
getConfig: (options: Options) => Promise;
corePresets?: string[];
overridePresets?: string[];
}
In development mode, the `start` API call is responsible for initializing the development server to monitor the file system for changes (for example, components and stories) then execute a hot module reload in the browser. It also provides a **bail** function to allow the running process to end gracefully, either via user input or error.
In production, the `build` API call is responsible for generating a static Storybook build, storing it by default in the `storybook-static` directory if no additional configuration is provided. The generated output should contain everything the user needs to view its Storybook by opening either the `index.html` or `iframe.html` in a browser with no other processes running.
##
Implementation
Under the hood, a builder is responsible for serving/building the preview `iframe`, which has its own set of requirements. To fully support Storybook, including the [essential features](../writing-stories) that ship with Storybook, it must consider the following.
###
Import stories
The `stories` configuration field enables story loading in Storybook. It defines an array of file globs containing the physical location of the component's stories. The builder must be able to load those files and monitor them for changes and update the UI accordingly.
###
Provide configuration options
By default, Storybook's configuration is handled in a dedicated file (`storybook/main.js|ts`), giving the user the option to customize it to suit its needs. The builder should also provide its own configuration support through additional fields or some other builder-appropriate mechanism. For example:
vite-server.ts
import { stringifyProcessEnvs } from './envs';
import { getOptimizeDeps } from './optimizeDeps';
import { commonConfig } from './vite-config';
import type { EnvsRaw, ExtendedOptions } from './types';
export async function createViteServer(options: ExtendedOptions, devServer: Server) {
const { port, presets } = options;
// Defines the baseline config.
const baseConfig = await commonConfig(options, 'development');
const defaultConfig = {
...baseConfig,
server: {
middlewareMode: true,
hmr: {
port,
server: devServer,
},
fs: {
strict: true,
},
},
optimizeDeps: await getOptimizeDeps(baseConfig, options),
};
const finalConfig = await presets.apply('viteFinal', defaultConfig, options);
const envsRaw = await presets.apply>('env');
// Remainder implementation
}
###
Handle preview.js exports
The [`preview.js`](../configure/index#configure-story-rendering) configuration file allows users to control how the story renders in the UI. This is provided via the [decorators](../writing-stories/decorators) named export. When Storybook starts, it converts these named exports into internal API calls via virtual module entry, for example, `addDecorator()`. The builder must also provide a similar implementation. For example:
import { virtualPreviewFile, virtualStoriesFile } from './virtual-file-names';
import { transformAbsPath } from './utils/transform-abs-path';
import type { ExtendedOptions } from './types';
export async function generateIframeScriptCode(options: ExtendedOptions) {
const { presets, frameworkPath, framework } = options;
const frameworkImportPath = frameworkPath || `@storybook/${framework}`;
const presetEntries = await presets.apply('config', [], options);
const configEntries = [...presetEntries].filter(Boolean);
const absoluteFilesToImport = (files: string[], name: string) =>
files
.map((el, i) => `import ${name ? `* as ${name}_${i} from ` : ''}'${transformAbsPath(el)}'`)
.join('\n');
const importArray = (name: string, length: number) =>
new Array(length).fill(0).map((_, i) => `${name}_${i}`);
const code = `
// Ensure that the client API is initialized by the framework before any other iframe code
// is loaded. That way our client-apis can assume the existence of the API+store
import { configure } from '${frameworkImportPath}';
import {
addDecorator,
addParameters,
addArgTypesEnhancer,
addArgsEnhancer,
setGlobalRender
} from 'storybook/preview-api';
import { logger } from 'storybook/internal/client-logger';
${absoluteFilesToImport(configEntries, 'config')}
import * as preview from '${virtualPreviewFile}';
import { configStories } from '${virtualStoriesFile}';
const configs = [${importArray('config', configEntries.length)
.concat('preview.default')
.join(',')}].filter(Boolean)
configs.forEach(config => {
Object.keys(config).forEach((key) => {
const value = config[key];
switch (key) {
case 'args':
case 'argTypes': {
return logger.warn('Invalid args/argTypes in config, ignoring.', JSON.stringify(value));
}
case 'decorators': {
return value.forEach((decorator) => addDecorator(decorator, false));
}
case 'parameters': {
return addParameters({ ...value }, false);
}
case 'render': {
return setGlobalRender(value)
}
case 'globals':
case 'globalTypes': {
const v = {};
v[key] = value;
return addParameters(v, false);
}
case 'decorateStory':
case 'renderToCanvas': {
return null;
}
default: {
// eslint-disable-next-line prefer-template
return console.log(key + ' was not supported :( !');
}
}
});
})
configStories(configure);
`.trim();
return code;
}
###
MDX support
[Storybook's Docs](../writing-docs) includes the ability to author stories/documentation in MDX using a Webpack loader. The builder must also know how to interpret MDX and invoke Storybook's special extensions. For example:
mdx-plugin.ts
import mdx from 'vite-plugin-mdx';
import { createCompiler } from 'storybook/internal/csf-tools/mdx';
export function mdxPlugin() {
return mdx((filename) => {
const compilers = [];
if (filename.endsWith('stories.mdx') || filename.endsWith('story.mdx')) {
compilers.push(createCompiler({}));
}
return {
compilers,
};
});
}
###
Generate source code snippets
Storybook annotates components and stories with additional metadata related to their inputs to automatically generate interactive controls and documentation. Currently, this is provided via Webpack loaders/plugins. The builder must re-implement this to support those features.
###
Generate a static build
One of Storybook's core features it's the ability to generate a static build that can be [published](../sharing/publish-storybook) to a web hosting service. The builder must also be able to provide a similar mechanism. For example:
build.ts
import { build as viteBuild } from 'vite';
import { stringifyProcessEnvs } from './envs';
import { commonConfig } from './vite-config';
import type { EnvsRaw, ExtendedOptions } from './types';
export async function build(options: ExtendedOptions) {
const { presets } = options;
const baseConfig = await commonConfig(options, 'build');
const config = {
...baseConfig,
build: {
outDir: options.outputDir,
emptyOutDir: false,
sourcemap: true,
},
};
const finalConfig = await presets.apply('viteFinal', config, options);
const envsRaw = await presets.apply>('env');
// Stringify env variables after getting `envPrefix` from the final config
const envs = stringifyProcessEnvs(envsRaw, finalConfig.envPrefix);
// Update `define`
finalConfig.define = {
...finalConfig.define,
...envs,
};
await viteBuild(finalConfig);
}
###
Development server integration
By default, when Storybook starts in development mode, it relies on its internal development server. The builder needs to be able to integrate with it. For example:
server.ts
import { createServer } from 'vite';
export async function createViteServer(options: ExtendedOptions, devServer: Server) {
const { port } = options;
// Remainder server configuration
// Creates the server.
return createServer({
// The server configuration goes here
server: {
middlewareMode: true,
hmr: {
port,
server: devServer,
},
},
});
}
###
Shutdown the development server
The builder must provide a way to stop the development server once the process terminates; this can be via user input or error. For example:
index.ts
import { createViteServer } from './vite-server';
let server: ViteDevServer;
export async function bail(): Promise {
return server?.close();
}
export const start: ViteBuilder['start'] = async ({ options, server: devServer }) => {
// Remainder implementation goes here
server = await createViteServer(options as ExtendedOptions, devServer);
return {
bail,
totalTime: process.hrtime(startTime),
};
};
###
HMR support
While running in development mode, the builder's development server must be able to reload the page once a change happens, either in a story, component, or helper function.
###
More information
This area is under rapid development, and the associated documentation is still in progress and subject to change. If you are interested in creating a builder, you can learn more about implementing a builder in Storybook by checking the source code for [Vite](https://github.com/storybookjs/storybook/tree/next/code/builders/builder-vite), [Webpack](https://github.com/storybookjs/storybook/tree/next/code/builders/builder-webpack5), or Modern Web's [dev-server-storybook](https://github.com/modernweb-dev/web/blob/master/packages/dev-server-storybook/src/serve/storybookPlugin.ts). When you're ready, open an [RFC](../contribute/RFC) to discuss your proposal with the Storybook community and maintainers.
**Learn more about builders**
* [Vite builder](./vite) for bundling with Vite
* [Webpack builder](./webpack) for bundling with Webpack
* Builder API for building a Storybook builder
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/builders/builder-api.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/builders/vite
# Page: /docs/builders/vite
# Vite
ReactVueAngularWeb ComponentsMore
Storybook Vite builder bundles your components and stories with [Vite](https://vitejs.dev/), a fast ESM bundler.
* For applications built with Vite: it allows reusing the existing configuration in Storybook.
* For applications built with Webpack: it provides faster startup and refresh times, with the disadvantage that your component's execution environment differs from your application.
##
Setup
If you ran `npx storybook@latest init` to include Storybook in your Vite application, the builder is already installed and configured for you. If you want, you can also opt into it manually.
Run the following command to install the builder.
npm
npm install @storybook/builder-vite --save-dev
Update your Storybook configuration (in `.storybook/main.js|ts`) to include the builder.
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite, angular, etc.)
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
builder: '@storybook/builder-vite', // 👈 The builder enabled here.
},
};
export default config;
##
Configuration
Out of the box, Storybook's Vite builder includes a set of configuration defaults for the supported frameworks, which are merged alongside your existing configuration file. For an optimal experience when using the Vite builder, we recommend applying any configuration directly inside Vite's configuration file (i.e., [`vite.config.js|ts`](https://vitejs.dev/config/)).
When Storybook loads, it automatically merges the configuration into its own. However, since different projects may have specific requirements, you may need to provide a custom configuration for Storybook. In such cases, you can modify your configuration file (`.storybook/main.js|ts`) and add the `viteFinal` configuration function as follows:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite, angular, etc.)
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
builder: '@storybook/builder-vite',
},
async viteFinal(config) {
// Merge custom configuration into the default config
const { mergeConfig } = await import('vite');
return mergeConfig(config, {
// Add dependencies to pre-optimization
optimizeDeps: {
include: ['storybook-dark-mode'],
},
});
},
};
export default config;
The asynchronous function [`viteFinal`](../api/main-config/main-config-vite-final) receives a `config` object with the default builder configuration and returns the updated configuration.
###
Environment-based configuration
If you need to customize the builder's configuration and apply specific options based on your environment, extend the `viteFinal` function as follows:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite, angular, etc.)
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
builder: '@storybook/builder-vite',
},
async viteFinal(config, { configType }) {
const { mergeConfig } = await import('vite');
if (configType === 'DEVELOPMENT') {
// Your development configuration goes here
}
if (configType === 'PRODUCTION') {
// Your production configuration goes here.
}
return mergeConfig(config, {
// Your environment configuration here
});
},
};
export default config;
###
Override the default configuration
By default, the Vite builder in Storybook searches for the Vite configuration file in the root directory of your Storybook project. However, you can customize it to look for the configuration file in a different location. For example:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
builder: {
name: '@storybook/builder-vite',
options: {
viteConfigPath: '../customVite.config.js',
},
},
},
};
export default config;
💡
If you do not want Storybook to load the Vite configuration file automatically, you can use the `viteConfigPath` option to point to a non-existent file.
###
TypeScript
If you need, you can also configure Storybook's Vite builder using TypeScript. Rename your `.storybook/main.js` to `.storybook/main.ts` and adjust it as follows:
CSF 3CSF Next 🧪
.storybook/main.ts
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs-vite, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
async viteFinal(config, options) {
// Add your configuration here
return config;
},
};
export default config;
* * *
##
Troubleshooting
###
Migrating from Webpack
Vite generally handles more use cases out of the box than Webpack. For example, loading styles just works for most projects. So, when migrating a Webpack-based project to Vite, you may find that you don't need all of your previous configuration.
We recommend starting with no Storybook-specific Vite configuration and only adding what you determine your project actually requires.
For reference, here is a Webpack configuration to handle loading graphql queries and its equivalent, using a plugin, in Vite:
With Webpack (CSF 3)With Vite (CSF 3)With Webpack (CSF Next 🧪)With Vite (CSF Next 🧪)
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-webpack5, nextjs, angular)
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
async webpackFinal(config) {
config.module?.rules?.push({
test: /\.(graphql|gql)$/,
include: [path.resolve('./lib/emails')],
exclude: /node_modules/,
loader: 'graphql-tag/loader',
});
config.module?.rules?.push({
test: /\.(graphql|gql)$/,
include: [path.resolve('./lib/schema')],
exclude: /node_modules/,
loader: 'raw-loader',
});
return config;
},
};
export default config;
###
Working directory not being detected
By default, the Vite builder enables Vite's [`server.fs.strict`](https://vitejs.dev/config/#server-fs-strict) option for increased security, defining the project's `root` to Storybook's configuration directory. If you need to override it, you can use the `viteFinal` function and adjust it.
###
ArgTypes are not generated automatically
Currently, [automatic argType inference](../api/arg-types#automatic-argtype-inference) is only available for React, Vue 3, and Svelte (JSDocs only). With React, the Vite builder defaults to `react-docgen`, a faster alternative to `react-docgen-typescript` for parsing React components. If you run into any issues, you can revert to `react-docgen-typescript` by updating your Storybook configuration file as follows:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite, angular, etc.)
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
builder: '@storybook/builder-vite',
},
typescript: {
// Enables the `react-docgen-typescript` parser.
// See https://storybook.js.org/docs/api/main-config/main-config-typescript for more information about this option.
reactDocgen: 'react-docgen-typescript',
},
};
export default config;
###
Interaction tests not working as expected
If you are migrating from a Webpack-based project, such as [CRA](https://create-react-app.dev/), to Vite, and you are [interaction testing](../writing-tests/interaction-testing), you may run into a situation where your tests fail to execute notifying you that the `window` object is not defined. To resolve this issue, you can create a `preview-head.html` file in your Storybook configuration directory and include the following:
.storybook/preview-head.html
**Learn more about builders**
* Vite builder for bundling with Vite
* [Webpack builder](./webpack) for bundling with Webpack
* [Builder API](./builder-api) for building a Storybook builder
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/builders/vite.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/builders/webpack
# Page: /docs/builders/webpack
# Webpack
ReactVueAngularWeb ComponentsMore
Storybook Webpack builder is the default builder for Storybook. This builder enables you to create a seamless development and testing experience for your components and provides an efficient way to develop UI components in isolation allowing you to leverage your existing Webpack configuration with Storybook.
##
Configure
By default, Storybook provides zero-config support for Webpack and automatically sets up a baseline configuration created to work with the most common use cases. However, you can extend your Storybook configuration file (i.e., `.storybook/main.js|ts`) and provide additional options to improve your Storybook's performance or customize it to your needs. Listed below are the available options and examples of how to use them.
Option| Description
---|---
`lazyCompilation`| Enables Webpack's experimental [`lazy compilation`](https://webpack.js.org/configuration/experiments/#experimentslazycompilation)
`core: { builder: { options: { lazyCompilation: true } } }`
`fsCache`| Configures Webpack's filesystem [caching](https://webpack.js.org/configuration/cache/#cachetype) feature
`core: { builder: { options: { fsCache: true } } }`
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-webpack5, nextjs, angular, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
builder: {
name: '@storybook/builder-webpack5',
options: {
fsCache: true,
lazyCompilation: true,
},
},
},
};
export default config;
###
Override the default configuration
Storybook's Webpack configuration is based on [Webpack 5](https://webpack.js.org/), allowing it to be extended to fit your project's needs. If you need to add a loader or a plugin, you can provide the `webpackFinal` configuration element in your [`.storybook/main.js|ts`](../configure/index#configure-your-storybook-project) file. The configuration element should export a function that receives the baseline configuration as the first argument and Storybook's options object as the second argument. For example:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-webpack5, nextjs, angular, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
webpackFinal: async (config, { configType }) => {
if (configType === 'DEVELOPMENT') {
// Modify config for development
}
if (configType === 'PRODUCTION') {
// Modify config for production
}
return config;
},
};
export default config;
When Storybook starts, it automatically merges the configuration into its own. However, when providing the `webpackFinal` configuration element, you're responsible for merging the configuration yourself. We recommend that you handle the changes to the `config` object responsibly, preserving both the `entry` and `output` properties.
####
Working with Webpack plugins
Another way to customize your Storybook configuration is to add a custom plugin or loader to help with code optimization, asset management, or other tasks. Nevertheless, since Storybook relies on the `HtmlWebpackPlugin` to generate the preview page, we recommend that you append the changes to the `config.plugins` array rather than overwriting it. For example:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-webpack5, nextjs, angular, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
webpackFinal: async (config) => {
config.plugins.push(/* ... */);
return config;
},
};
export default config;
Additionally, when working with Webpack loaders that don't explicitly include specific file extensions (i.e., via the `test` property), you should `exclude` the `.ejs` file extension for that loader.
###
Import a custom Webpack configuration
If you already have an existing Webpack configuration file that you need to reuse with Storybook, you can import it and merge it into the default configuration. For example:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-webpack5, nextjs, angular, etc.
import type { StorybookConfig } from '@storybook/your-framework';
import custom from '../webpack.config.js'; // 👈 Custom Webpack configuration being imported.
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
webpackFinal: async (config) => {
return {
...config,
module: { ...config.module, rules: [...config.module.rules, ...custom.module.rules] },
};
},
};
export default config;
💡
Projects scaffolded based on generators may require that you import their specific Webpack configuration files. We suggest reading your generator's documentation for more information.
###
Debug Webpack configuration
If you intend to debug the Webpack configuration used by Storybook, you can use the Storybook CLI to help you. If you're running in [development mode](../api/cli-options#dev), you can use the following command:
npm
npm run storybook -- --debug-webpack
Additionally, if you're generating a [static build](../api/cli-options#build) of your Storybook, you can use the following command:
npm
npm run build-storybook -- --debug-webpack
##
Compiler support
Storybook takes a compiler-agnostic approach to bundling. This allows you to bring your own application bundler (e.g., [Babel](https://babeljs.io/), [SWC](https://swc.rs/)) and ensures compatibility within the vast ecosystem of Webpack 5-based projects.
###
SWC
If your project is built using [SWC](https://swc.rs/), use the [`@storybook/addon-webpack5-compiler-swc`](https://storybook.js.org/addons/@storybook/addon-webpack5-compiler-swc) addon. This addon increases ecosystem compatibility with Webpack 5 projects while maintaining high performance. Run the following command to set up the addon automatically:
npm
npx storybook@latest add @storybook/addon-webpack5-compiler-swc
ℹ️
Additional options can be provided to customize the SWC configuration. See the [SWC API documentation](../api/main-config/main-config-swc) for more information.
When enabled, this addon adjusts the Webpack configuration to use the [`swc-loader`](https://swc.rs/docs/usage/swc-loader) for JavaScript and TypeScript files. Additionally, it will detect and use your project's SWC configuration.
###
Babel
If you're working with a project that relies on Babel's tooling to provide support for specific features, including TypeScript or other modern JavaScript features, you can use the [`@storybook/addon-webpack5-compiler-babel`](https://storybook.js.org/addons/@storybook/addon-webpack5-compiler-babel) addon to allow you to include them in your Storybook to ensure compatibility with your project. Run the following command to set up the addon automatically:
npm
npx storybook@latest add @storybook/addon-webpack5-compiler-babel
ℹ️
Additional options can be provided to customize the Babel configuration. See the [`babel` API documentation](../api/main-config/main-config-babel) for more information, or if you're working on an addon, the [`babelDefault` documentation](../api/main-config/main-config-babel-default) for more information.
When enabled, the addon will adjust the Webpack configuration to use the [`babel-loader`](https://webpack.js.org/loaders/babel-loader/) as the default loader for JavaScript and TypeScript files. Additionally, it will detect and use your project's Babel configuration.
##
Troubleshooting
###
TypeScript modules are not resolved within Storybook
Storybook's default Webpack configuration provides support for most project setups without the need for any additional configuration. Nevertheless, depending on your project configuration, or the framework of choice, you may run into issues with TypeScript modules not being resolved within Storybook when aliased from your [`tsconfig` file](https://www.typescriptlang.org/tsconfig). If you encounter this issue, you can use [`tsconfig-paths-webpack-plugin`](https://github.com/dividab/tsconfig-paths-webpack-plugin#tsconfig-paths-webpack-plugin) while extending Storybook's Webpack config as follows:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-webpack5, nextjs, angular, etc.
import type { StorybookConfig } from '@storybook/your-framework';
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
webpackFinal: async (config) => {
if (config.resolve) {
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve.extensions,
}),
];
}
return config;
},
};
export default config;
However, if you're working with a framework that provides a default aliasing configuration (e.g., Next.js, Nuxt) and you want to configure Storybook to use the same aliases, you may not need to install any additional packages. Instead, you can extend the default configuration of Storybook to use the same aliases provided by the framework. For example, to set up an alias for the `@` import path, you can add the following to your `.storybook/main.js|ts` file:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
import path from 'path';
// Replace your-framework with the framework you are using, e.g. react-webpack5, nextjs, angular, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
webpackFinal: async (config) => {
if (config.resolve) {
config.resolve.alias = {
...config.resolve.alias,
'@': path.resolve(process.cwd(), 'src'),
};
}
return config;
},
};
export default config;
###
Pre-bundled assets do not show in the Storybook UI
As Storybook relies on [esbuild](https://esbuild.github.io/) to build its internal manager, support for bundling assets with the `managerWebpack` will no longer have an impact on the Storybook UI. We recommend removing existing `managerWebpack` configuration elements from your Storybook configuration file and bundling assets other than images or CSS into JavaScript beforehand.
###
Storybook doesn't run with Webpack 4
Support for Webpack 4 has been removed and is no longer being maintained. If you're upgrading your Storybook, it will automatically use Webpack 5 and attempt to migrate your configuration. However, if you're working with a custom Webpack configuration, you may need to update it to work with Webpack 5. The migration process is necessary to ensure that your project runs smoothly with the latest version of Storybook. You can follow the instructions provided on the Webpack [website](https://webpack.js.org/migrate/5/) to update your configuration.
**Learn more about builders**
* [Vite builder](./vite) for bundling with Vite
* Webpack builder for bundling with Webpack
* [Builder API](./builder-api) for building a Storybook builder
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/builders/webpack.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/builders
# Page: /docs/builders
# Builders
Storybook, at its core, is powered by builders such as Webpack and Vite. These builders spin up a development environment, compile your code—Javascript, CSS, and MDX—into an executable bundle and update the browser in real-time.

##
CLI basics
Before diving into setting up Storybook's builders, let's look at how the CLI configures them. When you initialize Storybook (via `npx storybook@latest init`), the CLI automatically detects which builder to use based on your application. For example, if you're working with Vite, it will install the Vite builder. If you're working with Webpack, it installs the Webpack 5 builder by default.
Additionally, you can also provide a flag to Storybook's CLI and specify the builder you want to use:
npx storybook@latest init --builder
##
Manual setup
Storybook uses the Webpack 5 builder by default if you don't specify one. If you want to use a different builder in your application, these docs detail how you can set up Storybook's supported builders.
* [**Vite builder**](./builders/vite) for bundling your stories with Vite with near-instant HMR.
* [**Webpack**](./builders/webpack) for bundling your stories with Webpack with improved performance
* [**Rspack / Rsbuild**](https://github.com/rspack-contrib/storybook-rsbuild) for bundling your stories with blazing fast Rspack and Rsbuild.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/builders/index.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/configure/environment-variables
# Page: /docs/configure/environment-variables
# Environment variables
ReactVueAngularWeb ComponentsMore
You can use environment variables in Storybook to change its behavior in different “modes”. If you supply an environment variable prefixed with `STORYBOOK_`, it will be available in `process.env` when using Webpack, or `import.meta.env` when using the Vite builder:
STORYBOOK_THEME=red STORYBOOK_DATA_KEY=12345 npm run storybook
💡
Do not store any secrets (e.g., private API keys) or other types of sensitive information in your Storybook. Environment variables are embedded into the build, meaning anyone can view them by inspecting your files.
Then we can access these environment variables anywhere inside our preview JavaScript code like below:
node-envvite-env
console.log(process.env.STORYBOOK_THEME);
console.log(process.env.STORYBOOK_DATA_KEY);
You can also access these variables in your custom ``/`` using the substitution `%STORYBOOK_X%`, for example: `%STORYBOOK_THEME%` will become `red`.
💡
If using the environment variables as attributes or values in JavaScript, you may need to add quotes, as the value will be inserted directly, for example: ``.
##
Using .env files
You can also use `.env` files to change Storybook's behavior in different modes. For example, if you add a `.env` file to your project with the following:
STORYBOOK_DATA_KEY=12345
Then you can access this environment variable anywhere, even within your stories:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const ExampleStory: Story = {
args: {
propertyA: process.env.STORYBOOK_DATA_KEY,
},
};
###
With Vite
Out of the box, Storybook provides a [Vite builder](../builders/vite), which does not output Node.js globals like `process.env`. To access environment variables in Storybook (e.g., `STORYBOOK_`, `VITE_`), you can use `import.meta.env`. For example:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const ExampleStory: Story = {
args: {
propertyA: import.meta.env.STORYBOOK_DATA_KEY,
propertyB: import.meta.env.VITE_CUSTOM_VAR,
},
};
ℹ️
You can also use specific files for specific modes. Add a `.env.development` or `.env.production` to apply different values to your environment variables.
You can also pass these environment variables when you are [building your Storybook](../sharing/publish-storybook) with `build-storybook`.
Then they'll be hardcoded to the static version of your Storybook.
##
Using Storybook configuration
Additionally, you can extend your Storybook configuration file (i.e., [`.storybook/main.js|.ts`](../configure/index#configure-story-rendering)) and provide a configuration field that you can use to define specific variables (e.g., API URLs). For example:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
/*
* 👇 The `config` argument contains all the other existing environment variables.
* Either configured in an `.env` file or configured on the command line.
*/
env: (config) => ({
...config,
EXAMPLE_VAR: 'An environment variable configured in Storybook',
}),
};
export default config;
When Storybook loads, it will enable you to access them in your stories similar as you would do if you were working with an `env` file:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Default: Story = {
args: {
exampleProp: process.env.EXAMPLE_VAR,
},
};
##
Using environment variables to choose the browser
Storybook allows you to choose the browser you want to preview your stories. Either through a `.env` file entry or directly in your `storybook` script.
The table below lists the available options:
Browser| Example
---|---
Safari| `BROWSER="safari"`
Firefox| `BROWSER="firefox"`
Chromium| `BROWSER="chromium"`
💡
By default, Storybook will open a new Chrome window as part of its startup process. If you don't have Chrome installed, make sure to include one of the following options, or set your default browser accordingly.
##
Troubleshooting
###
Environment variables are not working
If you're trying to use framework-specific environment variables (e.g.,`VUE_APP_`), you may run into issues primarily due to the fact that Storybook and your framework may have specific configurations and may not be able to recognize and use those environment variables. If you run into a similar situation, you may need to adjust your framework configuration to make sure that it can recognize and use those environment variables. For example, if you're working with a Vite-based framework, you can extend the configuration file and enable the [`envPrefix`](https://vitejs.dev/config/shared-options.html#envprefix) option. Other frameworks may require a similar approach.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/configure/environment-variables.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/configure/story-layout
# Page: /docs/configure/story-layout
# Story layout
ReactVueAngularWeb ComponentsMore
The `layout` [parameter](../writing-stories/parameters) allows you to configure how stories are positioned in Storybook's Canvas tab.
##
Global layout
You can add the parameter to your [`./storybook/preview.js`](./index#configure-story-rendering), like so:
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
parameters: {
layout: 'centered',
},
};
export default preview;

In the example above, Storybook will center all stories in the UI. `layout` accepts these options:
* `centered`: center the component horizontally and vertically in the Canvas
* `fullscreen`: allow the component to expand to the full width and height of the Canvas
* `padded`: _(default)_ Add extra padding around the component
##
Component layout
You can also set it at a component level like so:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
// Sets the layout parameter component wide.
parameters: {
layout: 'centered',
},
} satisfies Meta;
export default meta;
##
Story layout
Or even apply it to specific stories like so:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const WithLayout: Story = {
parameters: {
layout: 'centered',
},
};
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/configure/story-layout.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/configure/story-rendering
# Page: /docs/configure/story-rendering
# Story rendering
ReactVueAngularWeb ComponentsMore
In Storybook, your stories render in a particular “preview” iframe (also called the Canvas) inside the larger Storybook web application. The JavaScript build configuration of the preview is controlled by a [builder](../builders) config, but you also may want to run some code for every story or directly control the rendered HTML to help your stories render correctly.
##
Running code for every story
Code executed in the preview file (`.storybook/preview.js|ts`) runs for every story in your Storybook. This is useful for setting up global styles, initializing libraries, or anything else required to render your components.
Here's an example of how you might use the preview file to initialize a library that must run before your components render:
.storybook/preview.ts
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
import { initialize } from '../lib/your-library';
initialize();
const preview: Preview = {
// ...
};
export default preview;
##
Adding to
If you need to add extra elements to the `head` of the preview iframe, for instance, to load static stylesheets, font files, or similar, you can create a file called [`.storybook/preview-head.html`](./index#configure-story-rendering) and add tags like this:
.storybook/preview-head.html
ℹ️
Storybook will inject these tags into the _preview iframe_ where your components render, not the Storybook application UI.
However, it's also possible to modify the preview head HTML programmatically using a preset defined in the `main.js` file. Read the [presets documentation](../addons/writing-presets#ui-configuration) for more information.
##
Adding to
Sometimes, you may need to add different tags to the ``. Helpful for adding some custom content roots.
You can accomplish this by creating a file called `preview-body.html` inside your `.storybook` directory and adding tags like this:
.storybook/preview-body.html
If using relative sizing in your project (like `rem` or `em`), you may update the base `font-size` by adding a `style` tag to `preview-body.html`:
.storybook/preview-body.html
ℹ️
Storybook will inject these tags into the _preview iframe_ where your components render, not the Storybook application UI.
Just like how you have the ability to customize the preview `head` HTML tag, you can also follow the same steps to customize the preview `body` with a preset. To obtain more information on how to do this, refer to the [presets documentation](../addons/writing-presets#ui-configuration).
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/configure/story-rendering.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/configure/styling-and-css
# Page: /docs/configure/styling-and-css
# Styling and CSS
ReactVueAngularWeb ComponentsMore
There are many ways to include CSS in a web application, and correspondingly there are many ways to include CSS in Storybook. Usually, it is best to try and replicate what your application does with styling in Storybook’s configuration.
##
CSS
Storybook supports importing CSS files in a few different ways. Storybook will inject these tags into the preview iframe where your components render, not the Storybook Manager UI. The best way to import CSS depends on your project's configuration and your preferences.
###
Import bundled CSS (Recommended)
All Storybooks are pre-configured to recognize imports for CSS files. To add global CSS for all your stories, import it in [`.storybook/preview.ts`](./index#configure-story-rendering). These files will be subject to HMR, so you can see your changes without restarting your Storybook server.
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
import '../src/styles/global.css';
const preview: Preview = {
parameters: {},
};
export default preview;
If your component files import their CSS files, this will work too. However, if you're using CSS processor tools like Sass or Postcss, you may need some more configuration.
###
Include static CSS
If you have a global CSS file that you want to include in all your stories, you can import it in [`.storybook/preview-head.html`](./story-rendering#adding-to-head). However, these files will not be subject to HMR, so you'll need to restart your Storybook server to see your changes.
.storybook/preview-head.html
##
CSS modules
###
Vite
Vite comes with CSS modules support out-of-the-box. If you have customized the CSS modules configuration in your `vite.config.js` this will automatically be applied to your Storybook as well. Read more about [Vite's CSS modules support](https://vitejs.dev/guide/features.html#css-modules).
###
Webpack
📣
Using `@storybook/nextjs`?
Storybook recreates your Next.js configuration, so you can use CSS modules in your stories without any extra configuration.
If you're using Webpack and want to use CSS modules, you'll need some extra configuration. We recommend installing [`@storybook/addon-styling-webpack`](https://storybook.js.org/addons/@storybook/addon-styling-webpack/) to help you configure these tools.
##
PostCSS
###
Vite
Vite comes with PostCSS support out-of-the-box. If you have customized the PostCSS configuration in your `vite.config.js` this will automatically be applied to your Storybook as well. Read more about [Vite's PostCSS support](https://vitejs.dev/guide/features.html#postcss).
###
Webpack
📣
Using `@storybook/nextjs`?
Storybook recreates your Next.js configuration, so you can use PostCSS in your stories without any extra configuration.
If you're using Webpack and want to use PostCSS, you'll need some extra configuration. We recommend installing [`@storybook/addon-styling-webpack`](https://storybook.js.org/addons/@storybook/addon-styling-webpack/) to help you configure these tools.
##
CSS pre-processors
###
Vite
Vite comes with Sass, Less, and Stylus support out-of-the-box. Read more about [Vite's CSS Pre-processor support](https://vitejs.dev/guide/features.html#css-pre-processors).
###
Webpack
📣
Using `@storybook/nextjs`?
Storybook recreates your Next.js configuration, so you can use Sass in your stories without any extra configuration.
If you're using Webpack and want to use Sass or less, you'll need some extra configuration. We recommend installing [`@storybook/addon-styling-webpack`](https://storybook.js.org/addons/@storybook/addon-styling-webpack/) to help you configure these tools. Or if you'd prefer, you can customize [Storybook's webpack configuration yourself](../builders/webpack#override-the-default-configuration) to include the appropriate loader(s).
##
CSS-in-JS
CSS-in-JS libraries are designed to use basic JavaScript, and they often work in Storybook without any extra configuration. Some libraries expect components to render in a specific rendering “context” (for example, to provide themes), which can be accomplished with `@storybook/addon-themes`'s [`withThemeFromJSXProvider` decorator](https://github.com/storybookjs/storybook/blob/next/code/addons/themes/docs/api.md#withthemefromjsxprovider).
##
Adding webfonts
###
`.storybook/preview-head.html`
If you need webfonts to be available, you may need to add some code to the [`.storybook/preview-head.html`](./story-rendering#adding-to-head) file. We recommend including any assets with your Storybook if possible, in which case you likely want to configure the [static file location](./integration/images-and-assets#serving-static-files-via-storybook-configuration).
###
`.storybook/preview.ts`
If you're using something like [`fontsource`](https://fontsource.org/) for your fonts, you can import the needed css files in your [`.storybook/preview.ts`](./index#configure-story-rendering) file.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/configure/styling-and-css.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/configure/telemetry
# Page: /docs/configure/telemetry
# Telemetry
Storybook collects completely anonymous data to help us improve user experience. Participation in this anonymous program is optional, and you may opt-out if you'd not like to share any information.
##
Why is telemetry collected?
Hundreds of thousands of developers use Storybook daily to build, test, and document components. Storybook is framework agnostic and integrates with the front-end ecosystem:
* **JavaScript frameworks** such as [React](https://reactjs.org/), [Vue 3](https://vuejs.org/), [Svelte](https://svelte.dev/) and [Solid](https://www.solidjs.com/)
* **Libraries** such as [Styled-Components](https://styled-components.com/), [Tailwind](https://tailwindcss.com/), [Redux](https://redux.js.org/)
* **Design tools** such as [Figma](https://figma.com/), [Sketch](https://www.sketch.com/), [Zeplin](https://zeplin.io/) and [InVision](https://www.invisionapp.com/)
* **Workflow tools** such as [Notion](https://www.notion.so/product), [Confluence](https://www.atlassian.com/software/confluence), and [Jira](https://www.atlassian.com/software/jira)
In the past, our improvement process relied on manually gathering feedback. But with a growing userbase and the need to support a wide variety of integrations, we need a more accurate method for gauging Storybook usage and pain points.
These telemetry data help us (the maintainers) to prioritize the highest impact projects. That allows us to keep up with trends in the front-end ecosystem and verify that our community's hard work achieves the intended result.
##
What is being collected?
We collect general usage details, including command invocation, Storybook version, addons, and the view layer.
Specifically, we track the following information in our telemetry events:
* Timestamp of the occurrence.
* Command invoked (e.g., `init`, `upgrade`, `dev`, `build`).
* Storybook unique identifier: One-way hash generated during Storybook installation process.
* One way hash of the IP address where the event occurred for spam detection.
* Story count.
* Storybook version.
* Storybook metadata:
* Language (e.g., TypeScript, JavaScript).
* Supported view layers (e.g., React, Vue 3, Angular, Svelte).
* Builder (e.g., Webpack5, Vite).
* Meta framework (e.g., [Next](https://nextjs.org/), [Gatsby](https://www.gatsbyjs.com/), [CRA](https://create-react-app.dev/)).
* [Addons](https://storybook.js.org/integrations) (e.g., [Accessibility](https://storybook.js.org/addons/@storybook/addon-a11y/)).
* Testing tools (e.g. [Jest](https://jestjs.io/), [Vitest](https://vitest.dev/), [Playwright](https://playwright.dev/)).
* Package manager information (e.g., `npm`, `yarn`).
* Monorepo information (e.g., [NX](https://nx.dev/), [Turborepo](https://turborepo.org/)).
* In-app events (e.g., [Storybook guided tour](https://github.com/storybookjs/addon-onboarding), [UI test run](../writing-tests/integrations/vitest-addon/index#storybook-ui)).
Access to the raw data is highly controlled, limited to select members of Storybook's core team who maintain the telemetry. We cannot identify individual users from the dataset: it is anonymized and untraceable back to the user.
##
What about sensitive information?
We take your privacy and our security very seriously. We perform additional steps to ensure that secure data (e.g., environment variables or other forms of sensitive data) **do not** make their way into our analytics. You can view all the information we collect by setting the `STORYBOOK_TELEMETRY_DEBUG` to `1` to print out the information gathered. For example:
npm
STORYBOOK_TELEMETRY_DEBUG=1 npm run storybook
Will generate the following output:
{
"anonymousId": "8bcfdfd5f9616a1923dd92adf89714331b2d18693c722e05152a47f8093392bb",
"eventType": "dev",
"payload": {
"versionStatus": "cached",
"storyIndex": {
"storyCount": 0,
"componentCount": 0,
"pageStoryCount": 0,
"playStoryCount": 0,
"autodocsCount": 0,
"mdxCount": 0,
"exampleStoryCount": 8,
"exampleDocsCount": 3,
"onboardingStoryCount": 0,
"onboardingDocsCount": 0,
"version": 5
},
"storyStats": {
"factory": 0,
"play": 0,
"render": 1,
"loaders": 0,
"beforeEach": 0,
"globals": 0,
"storyFn": 5,
"mount": 0,
"moduleMock": 0,
"tags": 0
}
},
"metadata": {
"generatedAt": 1689007841223,
"settingsCreatedAt": 1689007841223,
"hasCustomBabel": false,
"hasCustomWebpack": false,
"hasStaticDirs": false,
"hasStorybookEslint": false,
"refCount": 0,
"portableStoriesFileCount": 0,
"packageManager": {
"type": "yarn",
"version": "3.1.1"
},
"monorepo": "Nx",
"framework": {
"name": "@storybook/react-vite",
"options": {}
},
"builder": "@storybook/builder-vite",
"renderer": "@storybook/react",
"storybookVersion": "9.0.0",
"storybookVersionSpecifier": "^9.0.0",
"language": "typescript",
"storybookPackages": {
"@storybook/addon-docs/blocks": {
"version": "9.0.0"
},
"@storybook/react": {
"version": "9.0.0"
},
"@storybook/react-vite": {
"version": "9.0.0"
},
"storybook": {
"version": "9.0.0"
}
},
"addons": {
"@storybook/addon-onboarding": {
"version": "1.0.6"
}
}
}
}
Additionally, if Storybook's guided tour is enabled, it will generate the following output:
{
"eventType": "addon-onboarding",
"payload": {
"step": "1:Welcome",
"addonVersion": "1.0.6"
},
"metadata": {
// See above for metadata that's collected.
}
}
##
Will this data be shared?
The data we collect is anonymous, not traceable to the source, and only meaningful in aggregate form. No data we collect is personally identifiable. In the future, we plan to share relevant data with the community through public dashboards (or similar data representation formats).
##
How to opt-out
You may opt out of the telemetry within your Storybook configuration by setting the `disableTelemetry` configuration element to `true`.
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
disableTelemetry: true, // 👈 Disables telemetry
},
};
export default config;
If necessary, you can also turn off telemetry via the command line with the `--disable-telemetry` flag.
npm
npm run storybook -- --disable-telemetry
Or via the `STORYBOOK_DISABLE_TELEMETRY` environment variable.
STORYBOOK_DISABLE_TELEMETRY=true yarn storybook
💡
There is a `boot` event containing no metadata (used to ensure the telemetry is working). It is sent prior to evaluating your [Storybook configuration file](../api/main-config/main-config) (i.e., `main.js|ts`), so it is unaffected by the `disableTelemetry` option. If you want to ensure that the event is not sent, use the `STORYBOOK_DISABLE_TELEMETRY` environment variable.
##
Crash reports (disabled by default)
In addition to general usage telemetry, you may also choose to share crash reports. Storybook will then sanitize the error object (removing all user paths) and append it to the telemetry event. To enable crash reporting, you can set the `enableCrashReports` configuration element to `true`.
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
core: {
enableCrashReports: true, // 👈 Appends the crash reports to the telemetry events
},
};
export default config;
You can also enable crash reporting via the command line with the `--enable-crash-reports` flag.
npm
npm run storybook -- --enable-crash-reports
Or by setting the `STORYBOOK_ENABLE_CRASH_REPORTS` environment variable to `1`.
STORYBOOK_ENABLE_CRASH_REPORTS=1 yarn storybook
Enabling any of the options will generate the following item in the telemetry event:
{
stack: 'Error: Your button is not working\n' +
' at Object. ($SNIP/test.js:39:27)\n' +
' at Module._compile (node:internal/modules/cjs/loader:1103:14)\n' +
' at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)\n' +
' at Module.load (node:internal/modules/cjs/loader:981:32)\n' +
' at Function.Module._load (node:internal/modules/cjs/loader:822:12)\n' +
' at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)\n' +
' at node:internal/main/run_main_module:17:47',
message: 'Your button is not working'
}
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/configure/telemetry.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/configure
# Page: /docs/configure
# Configure Storybook
ReactVueAngularWeb ComponentsMore
Storybook is configured via a folder called `.storybook`, which contains various configuration files.
ℹ️
Note that you can change the folder that Storybook uses by setting the `-c` flag to your `storybook dev` and `storybook build` [CLI commands](./api/cli-options).
##
Configure your Storybook project
Storybook's main configuration (i.e., the `main.js|ts`) defines your Storybook project's behavior, including the location of your stories, the addons you use, feature flags and other project-specific settings. This file should be in the `.storybook` folder in your project's root directory. You can author this file in either JavaScript or [TypeScript](./configure/integration/typescript). Listed below are the available options and examples of how to use them.
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
// Required
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
// Optional
addons: ['@storybook/addon-docs'],
staticDirs: ['../public'],
};
export default config;
ℹ️
This configuration file is a [preset](./addons/addon-types) and, as such, has a powerful interface, which can be further customized. Read our documentation on writing [presets](./addons/writing-presets) to learn more.
Configuration element| Description
---|---
`stories`| The array of globs that indicates the location of your story files, relative to `main.js`
`staticDirs`| Sets a list of directories of [static files](./configure/integration/images-and-assets#serving-static-files-via-storybook-configuration) to be loaded by Storybook
`staticDirs: ['../public']`
`addons`| Sets the list of [addons](https://storybook.js.org/integrations) loaded by Storybook
`addons: ['@storybook/addon-docs']`
`typescript`| Configures how Storybook handles [TypeScript files](./configure/integration/typescript)
`typescript: { check: false, checkOptions: {} }`
`framework`| Configures Storybook based on a set of [framework-specific](./configure/integration/frameworks) settings
`framework: { name: '@storybook/svelte-vite', options:{} }`
`core`| Configures Storybook's [internal features](./api/main-config/main-config-core)
`core: { disableTelemetry: true, }`
`docs`| Configures Storybook's [auto-generated documentation](./writing-docs/autodocs)
`docs: { defaultName: 'Documentation' }`
`features`| Enables Storybook's [additional features](./api/main-config/main-config-features)
See table below for a list of available features
`refs`| Configures [Storybook composition](./sharing/storybook-composition)
`refs: { example: { title: 'ExampleStorybook', url:'https://your-url.com' } }`
`logLevel`| Configures Storybook's logs in the browser terminal. Useful for debugging
`logLevel: 'debug'`
`webpackFinal`| Customize Storybook's [Webpack](./builders/webpack) setup
`webpackFinal: async (config:any) => { return config; }`
`viteFinal`| Customize Storybook's Vite setup when using the [vite builder](https://github.com/storybookjs/builder-vite)
`viteFinal: async (config: Vite.InlineConfig, options: Options) => { return config; }`
`env`| Defines custom Storybook [environment variables](./configure/environment-variables#using-storybook-configuration).
`env: (config) => ({...config, EXAMPLE_VAR: 'Example var' }),`
`build`| Optimizes Storybook's production [build](./api/main-config/main-config-build) for performance by excluding specific features from the bundle. Useful when decreased build times are a priority.
`build: { test: {} }`
##
Configure story loading
By default, Storybook will load stories from your project based on a glob (pattern matching string) in `.storybook/main.js|ts` that matches all files in your project with extension `.stories.*`. The intention is for you to colocate a story file along with the component it documents.
•
└── components
├── Button.js
└── Button.stories.js
If you want to use a different naming convention, you can alter the glob using the syntax supported by [picomatch](https://github.com/micromatch/picomatch#globbing-features).
For example, if you wanted to pull both `.md` and `.js` files from the `my-project/src/components` directory, you could write:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../my-project/src/components/*.@(js|md)'],
};
export default config;
###
With a configuration object
Additionally, you can customize your Storybook configuration to load your stories based on a configuration object. For example, if you wanted to load your stories from a `packages/components` directory, you could adjust your `stories` configuration field into the following:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: [
{
// 👇 Sets the directory containing your stories
directory: '../packages/components',
// 👇 Storybook will load all files that match this glob
files: '*.stories.*',
// 👇 Used when generating automatic titles for your stories
titlePrefix: 'MyComponents',
},
],
};
export default config;
When Storybook starts, it will look for any file containing the `stories` extension inside the `packages/components` directory and generate the titles for your stories.
###
With a directory
You can also simplify your Storybook configuration and load the stories using a directory. For example, if you want to load all the stories inside a `packages/MyStories`, you can adjust the configuration as such:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
// 👇 Storybook will load all existing stories within the MyStories folder
stories: ['../packages/MyStories'],
};
export default config;
###
With a custom implementation
You can also adjust your Storybook configuration and implement custom logic to load your stories. For example, suppose you were working on a project that includes a particular pattern that the conventional ways of loading stories could not solve. In that case, you could adjust your configuration as follows:
CSF 3CSF Next 🧪
.storybook/main.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';
import type { StoriesEntry } from 'storybook/internal/types';
async function findStories(): Promise {
// your custom logic returns a list of files
}
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: async (list: StoriesEntry[]) => [
...list,
// 👇 Add your found stories to the existing list of story files
...(await findStories()),
],
};
export default config;
####
Known limitations
Because of the way stories are currently indexed in Storybook, loading stories on demand has a couple of minor limitations at the moment:
* [CSF formats](./api/csf) from version 1 to version 3 are supported.
* Custom `storySort` functions are allowed based on a restricted API.
##
Configure story rendering
To control the way stories are rendered and add global [decorators](./writing-stories/decorators#global-decorators) and [parameters](./writing-stories/parameters#global-parameters), create a `.storybook/preview.js` file. This is loaded in the Canvas UI, the “preview” iframe that renders your components in isolation. Use `preview.js` for global code (such as [CSS imports](./get-started/setup#render-component-styles) or JavaScript mocks) that applies to all stories.
The `preview.js` file can be an ES module and export the following keys:
* `decorators` \- an array of global [decorators](./writing-stories/decorators#global-decorators)
* `parameters` \- an object of global [parameters](./writing-stories/parameters#global-parameters)
* `globalTypes` \- definition of [globalTypes](./essentials/toolbars-and-globals#global-types-and-the-toolbar-annotation)
If you’re looking to change how to order your stories, read about [sorting stories](./writing-stories/naming-components-and-hierarchy#sorting-stories).
##
Configure Storybook’s UI
To control the behavior of Storybook’s UI (the **“manager”**), you can create a `.storybook/manager.js` file.
This file does not have a specific API but is the place to set [UI options](./configure/user-interface/features-and-behavior) and to configure Storybook’s [theme](./configure/user-interface/theming).
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/configure/index.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/contribute/RFC
# Page: /docs/contribute/RFC
# RFC process
The RFC (Request for Comment) process is intended to provide a consistent and controlled path for new features to enter the project. It helps ensure that new features are well-designed, well-implemented, and well-tested, and they do not conflict with the project's overall direction and scope.
##
Goal
Many changes, such as bug fixes and documentation improvements, can be implemented and reviewed via the normal GitHub pull request workflow. Some changes, however, are considered “substantial”, and we ask that these undergo a design process, solicit community input, and reach a consensus among the Storybook core team.
The purpose of the RFC (Request for Comment) process is to:
* Provide a transparent system for proposing new feature ideas.
* Establish a reliable and well-regulated process for introducing new features into the project.
* Provide a way for the community to participate in developing new features.
###
“Feature Request” vs. “RFC”
A _feature request_ is a straightforward and relatively informal way for Storybook users to suggest a new feature or enhancement to the project. While feature requests can provide valuable insights and ideas, they typically do not involve an in-depth design process or require consensus among the core team. Feature requests are usually open to discussion and may or may not be implemented based on factors like popularity, feasibility, and alignment with the project's goals.
On the other hand, an _RFC_ is a more formalized and structured process for proposing substantial changes or additions to the project. It involves following a defined set of steps to ensure that the proposed feature or modification receives proper consideration, design, and feedback. RFCs are typically used for changes that significantly impact the project, such as introducing new API functionality, removing existing features, or establishing new usage conventions. The RFC process aims to foster discussions, gather feedback from a wider audience, and reach consensus among the core team before integrating the proposed change into the project. Accepted RFCs are more likely to be implemented than regular feature requests.
##
The RFC lifecycle
###
1\. `Status: Proposed`
Open a new GitHub discussion in the [“RFC” category](https://github.com/storybookjs/storybook/discussions/new?category=rfc). Fill out the form as instructed.
_Details matter_ : RFCs that do not present convincing motivation, demonstrate a lack of understanding of the design's impact, or are disingenuous about the drawbacks or alternatives tend to be poorly received.
###
2\. `Status: In review`
RFCs tend to remain in this stage for a while, giving the community and core team members time to weigh in. During this period, the author of an RFC should be prepared to revise the proposal, integrate feedback, and build consensus. RFCs that have broad support are much more likely to make progress than those that don't receive any comments.
Every week, the Storybook core team conducts a triage meeting to review open RFCs as part of the meeting's agenda. The event is publicly scheduled in the [Storybook Discord](https://discord.gg/storybook) and held in the [Storybook Discord's Watercooler channel](https://discord.com/channels/486522875931656193/486522876388704260). We invite the RFC author(s) and interested members of the community to participate and engage in a more detailed discussion of the RFC. If a core team member deems it necessary, they will be assigned as the "champion" of the RFC. The champion will collaborate with the RFC author and assist them throughout the RFC process.
###
3\. `Status: accepted/rejected`
Eventually, the team will decide whether the RFC is a candidate for inclusion in Storybook. On the other hand, an RFC may be rejected by the team after a public discussion has settled and comments have been made summarizing the rationale for rejection.
##
Implementing an accepted RFC
The author of an RFC is not obligated to implement it. Of course, the RFC author (like any other developer) is welcome to post an implementation for review after the RFC has been accepted. However, note that the “accepted” status does not indicate priority nor whether it’s being actively worked on.
If you are interested in implementing an "active" RFC, but cannot determine if someone else is already working on it, feel free to ask (e.g., by leaving a comment on the associated issue).
This RFC process took heavy inspiration from the RFC processes from [Rust](https://github.com/rust-lang/rfcs) and [Gatsby](https://www.gatsbyjs.com/contributing/rfc-process/).
**Learn more about contributing to Storybook**
* RFC process for authoring feature requests
* [Code](./code) for features and bug fixes
* [Frameworks](./framework) to get started with a new framework
* [Documentation](./documentation/documentation-updates) for documentation improvements, typos, and clarifications
* [Examples](./documentation/new-snippets) for new snippets
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/contribute/RFC.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/contribute/code
# Page: /docs/contribute/code
# Code contributions
Contribute a new feature or bug fix to [Storybook's monorepo](https://github.com/storybookjs/storybook). This page outlines how to get your environment set up to contribute code.
##
Prerequisites
* Ensure you have Node version 18 installed (suggestion: v18.16.0).
* If you're working with Windows, all commands should be run in a terminal with administrator privileges.
##
Initial setup
Start by [forking](https://docs.github.com/en/github/getting-started-with-github/quickstart/fork-a-repo) the Storybook monorepo and cloning it locally.
git clone https://github.com/your-username/storybook.git
cd storybook
Storybook uses the [Yarn](https://yarnpkg.com/) package manager. Use [Corepack](https://github.com/nodejs/corepack) to set up the correct version for use with Storybook.
corepack enable
##
Run your first sandbox
Storybook development happens in a set of _sandboxes_ which are templated Storybook environments corresponding to different user setups. Within each sandbox, we inject a set of generalized stories that allow us to test core features and addons in all such environments.
To run a sandbox locally, you can use the `start` command:
yarn start
It will install the required prerequisites, build the code, create and link a starter example based on a Vite React setup and finally start the Storybook server.
If all goes well, you should see the sandbox running.

##
Running a different sandbox template
By default, the `start` command is configured to initialize a Vite-based React template. If you're planning on working on a different renderer instead, you can do so as well. Start by running the `task` command as follows:
yarn task
When prompted, answer the questions as accurately as possible to allow Storybook to determine your goals. After answering these questions, you should see the entire command with the options you've selected should you require to re-run it.
💡
The `yarn task` command takes a few development shortcuts that can catch you off guard when switching branches and may require you to re-run both the `install` and `compile` tasks. You can speed up the process by running the command with the `start-from=install` flag.
##
Running tests
After successfully running your first sandbox, you should have a fully functional Storybook version built on your local machine. Before jumping onto any code changes, verifying everything is working is essential—specifically, the test suite.
Run the following command to execute the tests:
yarn test
##
Start developing
Now that you've verified your setup, it's time to jump into code. The simplest way is to run one of the sandboxes in one terminal window and the interactive build process in a separate terminal.
Assuming you're still running the Vite-based React sandbox initialized after running the `yarn start` command, open a new terminal window and navigate to the `code` directory of the Storybook monorepo. Then, create a new branch for your contribution by running the following command:
git checkout -b my-first-storybook-contribution
Lastly, run the build process with the following:
yarn build
When prompted to start the build process in `watch` mode, answer **yes** to develop in interactive mode. Afterward, choose which packages you want to build. For example, if you're going to work on a feature for `@storybook/addon-docs`, you might want to select both `@storybook/addon-docs` and `storybook`.
💡
Build's `watch` mode is great for interactive development. However, for performance reasons, it only transpiles your code and doesn't execute the TypeScript compiler. If something isn't working as expected, try running the `build` command **WITHOUT** enabling watch mode: it will re-generate TypeScript types and perform automatic type checking for you.

If the work you'll be doing affects the `Preview` (the innermost Storybook `iframe`, where the stories are displayed), it will automatically refresh one to two seconds after you save.
Otherwise, if it affects the `Manager` (the outermost Storybook `iframe` where the addons are displayed), you'll need to refresh manually after saving.

##
Check your work
When you're done coding, add documentation and tests as appropriate. That simplifies the PR review process, which means your code will get merged faster.
###
Add stories
Adding a story or set of generic stories to our suite helps you test your work.
Assuming you're working on one of the [essential features](../essentials), there's a chance that a complete set of stories already exists. Check the addon's `template/stories` directory that documents how it's supposed to work and add your stories there.
If you're modifying something related to a specific renderer (e.g., React, Vue 3, etc.), it will also have a similar `template/stories` directory in which you'll need to add your stories.
###
Add tests
Unit tests ensure that Storybook doesn't break accidentally. If your code can regress in non-obvious ways, include unit tests with your pull request. Use the following naming convention:
+-- parentFolder
| +-- [filename].ts
| +-- [filename].test.ts
###
End-to-end tests (e2e)
Storybook's monorepo is set up to rely on end-to-end testing with [Playwright](https://playwright.dev) during CI. To help with testing, we encourage running this test suite before submitting your contribution.
To run an e2e test against a sandbox, you can use the `e2e-tests` task:
yarn task --task e2e-tests --template=react-vite/default-ts --start-from=auto
If there are issues and you'd like to debug them, you can pass a `DEBUG=1` environment variable, and Playwright will run in watch mode.
DEBUG=1 yarn task --task e2e-tests --template=react-vite/default-ts --start-from=auto
##
Submit a pull request
Before submitting your contribution, run the test suite one last time with the following:
yarn test
💡
Storybook relies on [Vitest](https://vitest.dev/) as part of it's testing suite. During the test run, if you spot that snapshot tests are failing, re-run the command with the `-u` flag to update them.
Doing this prevents last-minute bugs and is a great way to merge your contribution faster once you submit your pull request. Failing to do so will lead to one of the maintainers mark the pull request with the **Work in Progress** label until all tests pass.
###
Target `next` branch
Once the test suite finishes, it's time to commit, push and open a pull request against Storybook's `next` (default) branch. This branch is where all active development happens and is associated with the latest prerelease version (e.g., `7.0.0-alpha.47`).
If your contribution focuses on a bugfix and you want it featured in the next stable release, mention it in the pull request description. We'll try to patch it if it appears non-disruptive and fixes a critical bug.
####
Useful resources when working with forks
* [Sync a fork](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks/syncing-a-fork)
* [Merge an upstream repository into your fork](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks/merging-an-upstream-repository-into-your-fork)
###
Reproducing job failures
After creating your PR, if one of the CI jobs failed, when checking the logs of that job, you will see that it printed a message explaining how to reproduce the task locally. Typically that involves running the task against the right template:
yarn task --task e2e-tests --template=react-vite/default-ts --start-from=install
Typically it is a good idea to start from the `install` task to ensure your local code is completely up to date. If you reproduce the failure, you can try and make fixes, compile them with `build`, then rerun the task with `--start-from=auto`.
💡
The default instructions run the code in "linked" mode, meaning built changes to Storybook library code will be reflected in the sandbox immediately (the next time you run the task). However, CI runs in "unlinked" mode, which in rare cases, will behave differently.
If you are having trouble reproducing, try rerunning the command with the `--no-link` flag. If you need to do that, you'll need to run it with `--start-from=compile` after each code change.
##
How to work with reproductions
We encourage bug reports to include reproductions. In the same way that it's possible to develop interactively against example projects in the monorepo, it's also possible to develop against a reproduction repository.
To do so, run the following command in the root of the monorepo:
npx storybook@next link https://github.com/your-username/your-project.git
This command creates a project `../storybook-repros/your-project`, and automatically links it to your local Storybook code. After connecting it, you should be able to run Storybook and develop as mentioned above.
If you already have a reproduction on your local machine, you can similarly link it to your monorepo dev setup with the `--local` flag:
npx storybook@next link --local /path/to/local-repro-directory
💡
The `storybook link` command relies on [Yarn linking](https://yarnpkg.com/cli/link/) under the hood. It requires your local reproduction to be using [Yarn 2 or higher](https://yarnpkg.com/) as well, which is the case if you've already enabled it with the [`storybook sandbox`](./how-to-reproduce) command per our contribution guidelines. The process will fail if you're trying to link a non-Yarn 2 project.
##
Developing a template
The first step is to add an entry to `code/lib/cli-storybook/src/sandbox-templates.ts`, which is the master list of all repro templates:
'cra/default-js': {
name: 'Create React App (Javascript)',
script: 'npx create-react-app .',
inDevelopment: true,
expected: {
framework: '@storybook/cra',
renderer: '@storybook/react',
builder: '@storybook/builder-webpack5',
},
},
Add the `inDevelopment` flag until the PR is merged (you can fast-follow it with a second PR to remove the flag), as it'll make the development process much easier.
The **`key`** `cra/default-js` consists of two parts:
* The prefix is the tool that was used to generate the repro app
* The suffix is options that modify the default install, e.g. a specific version or options
The **`script`** field is what generates the application environment. The `.` argument is “the current working directory” which is auto-generated based on the key (e.g. `repros/cra/default-js/before-storybook`). The `{{beforeDir}}` key can also be used, which will be replaced by the path of that directory.
The rest of the fields are self-explanatory:
The **`skipTasks`** field exists because some sandboxes might not work properly in specific tasks temporarily, but we might still want to run the other tasks. For instance, a bug was introduced outside of our control, which fails only in the `test-runner` task.
The **`name`** field should contain a human readable name/description of the template.
The **`expected`** field reflects what framework/renderer/builder we expect `sb init` to generate. This is useful for assertions while generating sandboxes. If the template is generated with a different expected framework, for instance, it will fail, serving as a way to detect regressions.
###
Running a sandbox
If your template has a `inDevelopment` flag, it will be generated (locally) as part of the sandbox process. You can create the sandbox with the following command, where `` is replaced by the id of the selected template e.g. `cra/default-js`:
yarn task --task dev --template --start-from=install
Templates with `inDevelopment` will automatically run with `--no-link` flag as it is required for the local template generation to work.
Once the PR is merged, the template will be generated on a nightly cadence and you can remove the `inDevelopment` flag and the sandbox will pull the code from our templates repository.
##
Troubleshooting
`yarn build --all --watch` watches everything but is resource-intensive
It's troublesome to know which packages you'll change ahead of time, and watching them can be highly demanding, even on modern machines. If you're working on a powerful enough machine, you can use `yarn build --all --watch` instead of `yarn build`.
**Learn more about contributing to Storybook**
* [RFC process](./RFC) for authoring feature requests
* Code for features and bug fixes
* [Frameworks](./framework) to get started with a new framework
* [Documentation](./documentation/documentation-updates) for documentation improvements, typos, and clarifications
* [Examples](./documentation/new-snippets) for new snippets
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/contribute/code.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/contribute/framework
# Page: /docs/contribute/framework
# Contributing a Storybook framework
A Storybook framework is a node package that enables out-of-the-box support for either a metaframework (Next.js, NuxtJS, SvelteKit) or a combination of [builder](../builders) (Webpack, Vite) plus renderer (React, Angular, Vue 3, web components, etc).
For metaframeworks, the Storybook framework also takes care of additional configuration necessary to make Storybook behave similarly to apps generated by the metaframework. For example, `@storybook/nextjs` [recreates or mocks a number of features of Next.js apps](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md#supported-features) inside Storybook.
For your reference, you can view [all of the official Storybook frameworks](https://github.com/storybookjs/storybook/tree/next/code/frameworks), including their full source code and documentation.
##
How to make a framework
###
1\. Decide on a package name
The name should start with `storybook-framework-` and then correspond to what your framework supports. For example, a framework targeting SvelteKit would be `storybook-framework-svelte-kit` and a framework targeting Stencil with Vite would be `storybook-framework-stencil-vite`. When not targeting a metaframework, the naming convention is `storybook-framework--`.
###
2\. Consider what your framework will need to do
The goal is to make Storybook behave—out-of-the-box—as similarly as possible to the metaframework or builder-renderer combination you’re targeting.
For metaframeworks, this means attempting to recreate any builder or babel configuration provided by the metaframework. You should try to do so in a way that respects the user's existing project configuration as much as possible.
The library or libraries your framework supports may have different major versions available. Consider which versions of each library your framework will support. You will need to account for the changes within those different versions or split your framework into different versions/packages itself to support each library version. To speed up maintenance, please consider adding integration tests for the various library versions your framework supports.
###
3\. Write the documentation
Before writing any code, write a helpful README that contains installation instructions and a list of available features. Use the [README for `@storybook/nextjs`](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md) as a template. Writing the documentation first helps guide your other work.
###
4\. Author the framework itself
A framework can contain the following parts:
####
`package.json` ([example](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/package.json))
Because a framework is a node package, it must contain a `package.json` file. Here’s a template you can use to start:
`package.json` templatepackage.json
{
"name": "",
"version": "1.0.0",
"description": "Storybook for or & ",
"keywords": [
"Storybook",
"",
"",
"",
"",
"",
""
],
"homepage": "",
"bugs": {
"url": "https://github.com///issues"
},
"repository": {
"type": "git",
"url": "https://github.com//.git",
"directory": ""
},
"license": "MIT",
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./preset": {
"types": "./dist/preset.d.ts",
"require": "./dist/preset.js",
"import": "./dist/preset.mjs"
},
"./preview.js": {
"types": "./dist/preview.d.ts",
"require": "./dist/preview.js",
"import": "./dist/preview.mjs"
},
"./package.json": "./package.json"
},
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": ["dist/**/*", "types/**/*", "README.md", "*.js", "*.d.ts"],
"scripts": {
"check": "tsc --noEmit",
"test": "..."
},
"dependencies": {
"storybook": "^9.0.0",
"@storybook/": "^9.0.0",
"@storybook/": "^9.0.0"
},
"devDependencies": {
"typescript": "x.x.x",
"": "^x.x.x",
"": "^x.x.x"
},
"peerDependencies": {
"": "^x.x.x || ^x.x.x",
"": "^x.x.x || ^x.x.x",
"": "^x.x.x"
},
"engines": {
"node": ">=20.0.0"
},
"publishConfig": {
"access": "public"
}
}
A few notes on some of those properties:
* `exports`: The root, `./preset`, and `package.json` exports are required. If your framework has a `preview.js`, then that is required as well.
* `types`: We strongly encourage you to author your framework in TypeScript and distribute the types.
* `dependencies` and `devDependencies`: These are just examples. Yours may look quite different.
* `peerDependencies`: If your framework provides support for multiple versions of the libraries you’re targeting, be sure that is represented here.
####
`preset.js` ([example](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/src/preset.ts))
The [preset API](../addons/writing-presets) is where you will configure the Storybook core (which builder and renderer are used by your framework), the builder (via either the [`webpackFinal`](../builders/webpack#override-the-default-configuration) or [`viteFinal`](../builders/vite#configuration) export), babel (via the `babel` export), any necessary addons, and any available options for your framework.
####
`preview.js` ([example](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/src/preview.tsx))
The (optional) [preview API](../configure/index#configure-story-rendering) is where you configure the rendering of stories, such as global decorators or initializing some runtime config needed for your framework to behave as expected. If your framework requires this file, note that you also need to [configure the `previewAnnotations` in `preset.js`](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/src/preset.ts#L71-L74).
####
`types.ts` ([example](https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/src/types.ts))
If you author your framework in TypeScript (recommended), you should export the type for `StorybookConfig` which reflects the available options of your framework.
###
5\. Test your framework
Test it in a fresh project using a Storybook set up as close as possible to your framework. For example, for `@storybook/nextjs`, which uses React and Webpack5, start with a project that uses `@storybook/react` and `@storybook/builder-webpack5`. Follow the installation instructions from your README and ensure everything works as expected. Remember to test the various versions, configs, and options for the libraries you’re supporting.
###
6\. Let us know!
Once it's fully tested and released, please let us know about your framework by either announcing it in the [`#showcase`](https://discord.com/channels/486522875931656193/1048740936953376859) Discord channel or tweeting it and mentioning `@storybookjs`. It's our hope that well-made community frameworks can eventually move into the Storybook codebase and be considered "officially" supported.
**Learn more about contributing to Storybook**
* [RFC process](./RFC) for authoring feature requests
* [Code](./code) for features and bug fixes
* Frameworks to get started with a new framework
* [Documentation](./documentation/documentation-updates) for documentation improvements, typos, and clarifications
* [Examples](./documentation/new-snippets) for new snippets
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/contribute/framework.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/contribute/how-to-reproduce
# Page: /docs/contribute/how-to-reproduce
# Create a reproduction
A reproducible test case lets you isolate the cause of a problem, which is the first step towards fixing it! This page outlines how to get set up to create a reproduction.
##
Why should you create a reproduction?
A reproducible test case is a great way to share a specific set of conditions that causes a bug. It allows both the maintainers and the community to verify, narrow down the cause of the problem and help you fix the issue.
##
Pre-requisites
Make sure you have:
* Installed [`Yarn`](https://yarnpkg.com/) on your local development machine.
* A [GitHub account](https://github.com/signup) for hosting the reproduction's code.
* A [Chromatic account](https://www.chromatic.com/start/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook) for publishing your Storybook.
##
Initial setup
First, open a terminal and run the following command:
npx storybook@next sandbox
💡
You can append a template name in the command to get filtered results (e.g., `npx storybook@next sandbox react`).
Next, choose the template you want to work with:

Finally, enter a location for your reproduction:

💡
If you don't provide a full path for the reproduction it will be generated in the current directory.
If everything worked as it should, you should have a fully functional Storybook set up in your local environment.
##
Third-party dependencies & addons
Before adding code, install and configure any necessary packages. For example, if you run into a problem with a CSS framework (e.g., [Tailwind](https://tailwindcss.com/)), you should install and configure it.
Install and configure any Storybook [addons](https://storybook.js.org/addons/) that relate to the issue (e.g.,`@storybook/addon-a11y`).
##
Add stories
Any Storybook reproduction wouldn't be complete without [stories](../writing-stories). To help fix your issue faster, we encourage you to include the minimum amount of stories that will replicate your issue.
##
Host
When you've finished your work, you'll need to host your reproduction. Start by signing into GitHub.com and create a [new repository](https://github.com/new).

Then, follow GitHub's instructions to set up the repository.

💡
Don't forget to replace `your-username` with your own account name.
##
Publish
An excellent way to check your reproduction is to have it deployed online. We recommend [Chromatic](https://www.chromatic.com/?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook), a free publishing service created by the Storybook maintainers. It allows you to deploy and host your reproduction safely and securely in the cloud.
###
Helpful resources when working with Chromatic
* [Publish Storybook](../sharing/publish-storybook)
* [Setup Chromatic](https://www.chromatic.com/docs/setup?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook)
* [Automate Chromatic with continuous integration](https://www.chromatic.com/docs/ci?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook)
##
Submit the issue
Finally, create your issue in the [Storybook issue tracker](https://github.com/storybookjs/storybook/issues/new/choose), go through the required steps, and provide a detailed description of the problem. Add the GitHub repository and [deployed reproduction](https://www.chromatic.com/docs/setup?utm_source=storybook_website&utm_medium=link&utm_campaign=storybook#view-published-storybook) to help with the triage process.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/contribute/how-to-reproduce.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/contribute
# Page: /docs/contribute
# How to contribute
Storybook is a community-oriented open source project that welcomes contributions. Some of our most popular features started with a developer wanting to solve a problem for themselves.
##
Contributor covenant
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. [Continue reading our contributor covenant »](https://github.com/storybookjs/storybook/blob/next/CODE_OF_CONDUCT.md)
##
Ways to contribute
* [**RFC process**](./contribute/RFC) for authoring feature requests
* [**Code**](./contribute/code) for features and bug fixes
* [**Frameworks**](./contribute/framework) to get started with a new framework
* [**Documentation**](./contribute/documentation/documentation-updates) for documentation improvements, typos, and clarifications
* [**Examples**](./contribute/documentation/new-snippets) for new snippets and examples
* [**Addons**](./addons) for new addons
##
Not sure how to get started?
* [Chat in Discord `#contributing`](https://discord.com/channels/486522875931656193/839297503446695956)
* [Browse "good first issues" to fix](https://github.com/storybookjs/storybook/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
* [Submit a bug report or feature request](https://github.com/storybookjs/storybook/issues)
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/contribute/index.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/essentials/actions
# Page: /docs/essentials/actions
# Actions
ReactVueAngularWeb ComponentsMore
Actions are used to show that an event handler (callback) has been called, and to display its arguments. The actions panel can show both story args and other function calls.
##
Story args
Actions work via supplying special Storybook-generated mock functions to your story's event handler args. There are two ways to get an action arg:
###
Via storybook/test fn spies
The recommended way to write actions is to use the `fn` utility from `storybook/test` to mock and spy args. This is very useful for writing [interaction tests](../writing-tests/interaction-testing). You can mock your component's methods by assigning them to the `fn()` function:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { fn } from 'storybook/test';
import { Button } from './Button';
const meta = {
component: Button,
// 👇 Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked
args: { onClick: fn() },
} satisfies Meta;
export default meta;
If your component calls an arg (because of either the user's interaction or the `play` function) and that arg is spied on, the event will show up in the action panel:

###
Automatically matching args
Another option is to use a global parameter to match all [argTypes](../api/arg-types) that match a certain pattern. The following configuration automatically creates actions for each `on` argType (which you can either specify manually or can be [inferred automatically](../api/arg-types#automatic-argtype-inference)).
This is quite useful when your component has dozens (or hundreds) of methods and you do not want to manually apply the `fn` utility for each of those methods. However, **this is not the recommended** way of writing actions. That's because automatically inferred args **are not available as spies in your play function**. If you use `argTypesRegex` and your stories have play functions, you will need to also define args with the `fn` utility to test them in your play function.
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on.*' },
},
};
export default preview;
If you need more granular control over which `argTypes` are matched, you can adjust your stories and include the `argTypesRegex` parameter. For example:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
parameters: { actions: { argTypesRegex: '^on.*' } },
} satisfies Meta;
export default meta;
This will bind a standard HTML event handler to the outermost HTML element rendered by your component and trigger an action when the event is called for a given selector. The format is ``. The selector is optional; it defaults to all elements.
##
Non-story function calls
You can still use the actions panel if you need to log function calls that are unrelated to any story. This can be helpful for debugging or logging purposes. There are two main ways to do this: `spyOn` from `storybook/test` or the `action` function from `storybook/actions`. For basic logging, we recommend creating a function spy, and for more complex scenarios, you can use the `action` function directly.
###
Via storybook/test spyOn
Mocks and spies from `storybook/test` are automatically logged as actions. The easiest way to show function calls in the actions panel is to use the `spyOn` utility function. Spies appear with a default name, which you can customize by calling the `mockName` method.
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, svelte)
import type { Preview } from '@storybook/your-framework';
import { spyOn } from 'storybook/test';
const preview: Preview = {
async beforeEach() {
spyOn(console, 'log').mockName('console.log');
},
};
export default preview;
###
Via the `action` function
To filter which function calls are logged, you can override the `spyOn` function's behavior by providing a custom implementation that calls the `action` function from `storybook/actions` only if it matches a specific condition to prevent it from logging all calls to the function it spies on.
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, svelte)
import type { Preview } from '@storybook/your-framework';
import { action } from 'storybook/actions';
import { spyOn } from 'storybook/test';
const originalConsoleLog = console.log;
const preview: Preview = {
async beforeEach() {
spyOn(console, 'log')
// Disable automatic logging in the actions panel
.mockName('')
.mockImplementation((args) => {
// Check if the log message matches a certain pattern
if (someCondition(args)) {
// Manually log an action
action('console.log')(args);
}
// Call the original console.log function
originalConsoleLog(...args);
});
},
};
export default preview;
##
API
###
Parameters
This contributes the following [parameters](../writing-stories/parameters) to Storybook, under the `actions` namespace:
####
`argTypesRegex`
Type: `string`
Create actions for each arg that matches the regex. Please note the significant limitations of this approach, as described above.
####
`disable`
Type: `boolean`
Disable the action panel.
This parameter is most useful to allow overriding at more specific levels. For example, if this parameter is set to `true` at the project level, it could then be re-enabled by setting it to `false` at the meta (component) or story level.
###
Exports
import { action } from 'storybook/actions';
####
`action`
Type: `(name?: string) => void`
Allows you to create an action that appears in the actions panel of the Storybook UI when clicked. The action function takes an optional name parameter, which is used to identify the action in the UI.
CSF 3CSF Next 🧪
Button.stories.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { action } from 'storybook/actions';
import Button from './Button';
const meta = {
component: Button,
args: {
// 👇 Create an action that appears when the onClick event is fired
onClick: action('on-click'),
},
} satisfies Meta;
export default meta;
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/essentials/actions.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/essentials/backgrounds
# Page: /docs/essentials/backgrounds
# Backgrounds
ReactVueAngularWeb ComponentsMore
The backgrounds feature allows you to set the background color on which the story renders in the UI:

##
Configuration
By default, the backgrounds feature includes a light and dark background.
But you're not restricted to these backgrounds. You can configure your own set of colors with the `backgrounds` [parameter](../writing-stories/parameters) in your [`.storybook/preview.js|ts`](../configure/index#configure-story-rendering).
You can define the available background colors using the `options` property and set the initial background color using the `initialGlobals` property:
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
parameters: {
backgrounds: {
options: {
// 👇 Default options
dark: { name: 'Dark', value: '#333' },
light: { name: 'Light', value: '#F7F9F2' },
// 👇 Add your own
maroon: { name: 'Maroon', value: '#400' },
},
},
},
initialGlobals: {
// 👇 Set the initial background color
backgrounds: { value: 'light' },
},
};
export default preview;
##
Defining the background for a story
The backgrounds feature enables you to change the background color applied to a story by selecting from the list of predefined background colors in the toolbar. If needed, you can set a story to default to a specific background color, by using the `globals` option:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// 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 { Button } from './Button';
const meta = {
component: Button,
globals: {
// 👇 Set background value for all component stories
backgrounds: { value: 'gray', grid: false },
},
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const OnDark: Story = {
globals: {
// 👇 Override background value for this story
backgrounds: { value: 'dark' },
},
};
ℹ️
When you specify a background color for a story (or a component's stories) using `globals`, the color is applied and cannot be changed using the toolbar. This is useful to ensure a story is always rendered on a specific background color.
##
Extending the configuration
You can also configure backgrounds on a per-component or per-story basis through [parameter inheritance](../writing-stories/parameters#component-parameters).
To set the available background colors, use the `options` property. In this example, we'll adjust the colors for all of the Button component's stories:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
parameters: {
backgrounds: {
options: {
// 👇 Override the default `dark` option
dark: { name: 'Dark', value: '#000' },
// 👇 Add a new option
gray: { name: 'Gray', value: '#CCC' },
},
},
},
} satisfies Meta;
export default meta;
##
Disable backgrounds
If you want to turn off backgrounds in a story, you can do so by configuring the `backgrounds` parameter like so:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Large: Story = {
parameters: {
backgrounds: { disable: true },
},
};
##
Grid
The backgrounds feature also includes a Grid selector, which allows you to quickly see if your components are aligned.
You don't need additional configuration to get started. But its properties are fully customizable; if you don't supply any value to any of its properties, they'll default to the following values:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
// To apply a set of backgrounds to all stories of Button:
const meta = {
component: Button,
parameters: {
backgrounds: {
grid: {
cellSize: 20,
opacity: 0.5,
cellAmount: 5,
offsetX: 16, // Default is 0 if story has 'fullscreen' layout, 16 if layout is 'padded'
offsetY: 16, // Default is 0 if story has 'fullscreen' layout, 16 if layout is 'padded'
},
},
},
} satisfies Meta;
export default meta;
##
API
###
Globals
This module contributes the following globals to Storybook, under the `backgrounds` namespace:
####
`grid`
Type: `boolean`
Whether the grid is displayed.
####
`value`
Type: `string`
When set, the background color is applied and cannot be changed using the toolbar. Must match the key of one of the available colors.
###
Parameters
This module contributes the following [parameters](../writing-stories/parameters) to Storybook, under the `backgrounds` namespace:
####
`disable`
Type: `boolean`
Disable this feature's behavior. If you wish to disable this feature for the entire Storybook, you should [do so in your main configuration file](./index#disabling-features).
This parameter is most useful to allow overriding at more specific levels. For example, if this parameter is set to `true` at the project level, it could then be re-enabled by setting it to `false` at the meta (component) or story level.
####
`grid`
Type:
{
cellAmount?: number;
cellSize?: number;
disable?: boolean;
offsetX?: number;
offsetY?: number;
opacity?: number;
}
Configuration for the background grid.
##### `grid.cellAmount`
Type: `number`
Default: `5`
Specify the size of the minor grid lines.
##### `grid.cellSize`
Type: `number`
Default: `20`
Specify the size of the major grid lines.
##### `grid.disable`
Type: `boolean`
Turn off the grid.
##### `grid.offsetX`
Type: `number`
Default: `0` if [story layout](../api/parameters#layout) is `'fullscreen'`; `16` if story layout is `'padded'`
Horizontal offset of the grid.
##### `grid.offsetY`
Type: `number`
Default: `0` if [story layout](../api/parameters#layout) is `'fullscreen'`; `16` if story layout is `'padded'`
Vertical offset of the grid.
##### `grid.opacity`
Type: `number`
Default: `0.5`
The opacity of the grid lines.
####
`options`
(Required, see description)
Type:
{
[key: string]: {
name: string;
value: string;
};
}
Available background colors. See above for a usage example.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/essentials/backgrounds.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/essentials/controls
# Page: /docs/essentials/controls
# Controls
ReactVueAngularWeb ComponentsMore
Storybook Controls gives you a graphical UI to interact with a component's arguments dynamically without needing to code. Use the Controls panel to edit the inputs to your stories and see the results in real-time. It's a great way to explore your components and test different states.
Controls do not require any modification to your components. Stories for controls are:
* Convenient. Auto-generate controls based on React/Vue/Angular/etc. components.
* Portable. Reuse your interactive stories in documentation, tests, and even in designs.
* Rich. Customize the controls and interactive data to suit your exact needs.
To use Controls, you need to write your stories using [args](../writing-stories/args). Storybook will automatically generate UI controls based on your args and what it can infer about your component. Still, you can configure the controls further using [argTypes](../api/arg-types), see below.
💡
If you have stories in the older pre-Storybook 6 style, check the [args & controls migration guide](https://medium.com/storybookjs/storybook-6-migration-guide-200346241bb5) to learn how to convert your existing stories for args.
##
Choosing the control type
By default, Storybook will choose a control for each arg based on its initial value. This will work well with specific arg types (e.g., `boolean` or `string`). To enable them, add the `component` annotation to the default export of your story file, and it will be used to infer the controls and auto-generate the matching [`argTypes`](../api/arg-types) for your component using [`react-docgen`](https://github.com/reactjs/react-docgen), a documentation generator for React components that also includes first-class support for TypeScript.
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
For instance, suppose you have a `variant` arg on your story that should be `primary` or `secondary`:
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Primary: Story = {
args: {
variant: 'primary',
},
};
By default, Storybook will render a free text input for the `variant` arg:

It works as long as you type a valid string into the auto-generated text control. Still, it's not the best UI for our scenario, given that the component only accepts `primary` or `secondary` as variants. Let’s replace it with Storybook’s radio component.
We can specify which controls get used by declaring a custom [argType](../api/arg-types) for the `variant` property. ArgTypes encode basic metadata for args, such as name, description, and defaultValue for an arg. These get automatically filled in by Storybook Docs.
`ArgTypes` can also contain arbitrary annotations, which the user can override. Since `variant` is a component property, let's put that annotation on the default export.
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
argTypes: {
variant: {
options: ['primary', 'secondary'],
control: { type: 'radio' },
},
},
} satisfies Meta;
export default meta;
💡
ArgTypes are a powerful feature that can be used to customize the controls for your stories. For more information, see the documentation about customizing controls with `argTypes` annotation.
This replaces the input with a radio group for a more intuitive experience.

##
Custom control type matchers
Controls can automatically be inferred from arg's name with [regex](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp), but currently only for the color picker and date picker controls. If you've used the Storybook CLI to setup your project, it should have automatically created the following defaults in `.storybook/preview.js|ts`:
Control| Default regex| Description
---|---|---
**color**| `/(background|color)$/i`| Will display a color picker UI for the args that match it
**date**| `/Date$/`| Will display a date picker UI for the args that match it
If you haven't used the CLI to set the configuration, or if you want to define your patterns, use the `matchers` property in the `controls` parameter:
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;
##
Fully custom args
Until now, we only used auto-generated controls based on the component for which we're writing stories. If we are writing [complex stories](../writing-stories/stories-for-multiple-components), we may want to add controls for args that aren’t part of the component. For example, here's how you could use a `footer` arg to populate a child component:
CSF 3CSF Next 🧪
Page.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { Page } from './Page';
type PagePropsAndCustomArgs = React.ComponentProps & { footer?: string };
const meta = {
component: Page,
render: ({ footer, ...args }) => (
),
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const CustomFooter = {
args: {
footer: 'Built with Storybook',
},
} satisfies Story;
By default, Storybook will add controls for all args that:
* It infers from the component definition [if your framework supports it](../configure/integration/frameworks-feature-support).
* Appear in the list of args for your story.
Using `argTypes`, you can change the display and behavior of each control.
###
Dealing with complex values
When dealing with non-primitive values, you'll notice that you'll run into some limitations. The most obvious issue is that not every value can be represented as part of the `args` param in the URL, losing the ability to share and deep link to such a state. Beyond that, complex values such as JSX cannot be synchronized between the manager (e.g., the Controls panel) and the preview (your story).
One way to deal with this is to use primitive values (e.g., strings) as arg values and add a custom `render` function to convert them to their complex counterpart before rendering. It isn't the nicest way to do it (see below), but certainly the most flexible.
CSF 3CSF Next 🧪
YourComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { YourComponent } from './your-component';
const meta = {
component: YourComponent,
//👇 Creates specific argTypes with options
argTypes: {
propertyA: {
options: ['Item One', 'Item Two', 'Item Three'],
control: { type: 'select' }, // Automatically inferred when 'options' is defined
},
propertyB: {
options: ['Another Item One', 'Another Item Two', 'Another Item Three'],
},
},
} satisfies Meta;
export default meta;
type Story = StoryObj;
const someFunction = (valuePropertyA, valuePropertyB) => {
// Do some logic here
};
export const ExampleStory: Story = {
render: (args) => {
const { propertyA, propertyB } = args;
//👇 Assigns the function result to a variable
const someFunctionResult = someFunction(propertyA, propertyB);
return ;
},
args: {
propertyA: 'Item One',
propertyB: 'Another Item One',
},
};
Unless you need the flexibility of a function, an easier way to map primitives to complex values before rendering is to define a `mapping`; additionally, you can specify `control.labels` to configure custom labels for your checkbox, radio, or select input.
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
import { ArrowUp, ArrowDown, ArrowLeft, ArrowRight } from './icons';
const arrows = { ArrowUp, ArrowDown, ArrowLeft, ArrowRight };
const meta = {
component: Button,
argTypes: {
arrow: {
options: Object.keys(arrows), // An array of serializable values
mapping: arrows, // Maps serializable option values to complex arg values
control: {
type: 'select', // Type 'select' is automatically inferred when 'options' is defined
labels: {
// 'labels' maps option values to string labels
ArrowUp: 'Up',
ArrowDown: 'Down',
ArrowLeft: 'Left',
ArrowRight: 'Right',
},
},
},
},
} satisfies Meta;
export default meta;
Note that both `mapping` and `control.labels` don't have to be exhaustive. If the currently selected option is not listed, it's used verbatim.
##
Creating and editing stories from controls
You can create or edit stories, directly from the Controls panel.
###
Create a new story
Open the Controls panel for a story and adjust the value of a control. Then save those changes as a new story.
If you're working on a component that does not yet have any stories, you can click the ➕ button in the sidebar to search for your component and have a basic story created for you.
###
Edit a story
You can also update a control's value, then save the changes to the story. The story file's code will be updated for you.
###
Disable creating and editing of stories
If you don't want to allow the creation or editing of stories from the Controls panel, you can disable this feature by setting the `disableSaveFromUI` parameter to `true` in the `parameters.controls` parameter in your `.storybook/preview.js|ts` file.
##
Configuration
Controls can be configured in two ways:
* Individual controls can be configured via control annotations.
* The panel's appearance can be configured via parameters.
###
Annotation
As shown above, you can configure individual controls with the “control" annotation in the [argTypes](../api/arg-types) field of either a component or story. Below is a condensed example and table featuring all available controls.
Data Type| Control| Description
---|---|---
**boolean**| `boolean`| Provides a toggle for switching between possible states.
`argTypes: { active: { control: 'boolean' }}`
**number**| `number`| Provides a numeric input to include the range of all possible values.
`argTypes: { even: { control: { type: 'number', min:1, max:30, step: 2 } }}`
| `range`| Provides a range slider component to include all possible values.
`argTypes: { odd: { control: { type: 'range', min: 1, max: 30, step: 3 } }}`
**object**| `object`| Provides a JSON-based editor component to handle the object's values.
Also allows edition in raw mode.
`argTypes: { user: { control: 'object' }}`
**array**| `object`| Provides a JSON-based editor component to handle the array's values.
Also allows edition in raw mode.
`argTypes: { odd: { control: 'object' }}`
| `file`| Provides a file input component that returns an array of URLs.
Can be further customized to accept specific file types.
`argTypes: { avatar: { control: { type: 'file', accept: '.png' } }}`
**enum**| `radio`| Provides a set of radio buttons based on the available options.
`argTypes: { contact: { control: 'radio', options: ['email', 'phone', 'mail'] }}`
| `inline-radio`| Provides a set of inlined radio buttons based on the available options.
`argTypes: { contact: { control: 'inline-radio', options: ['email', 'phone', 'mail'] }}`
| `check`| Provides a set of checkbox components for selecting multiple options.
`argTypes: { contact: { control: 'check', options: ['email', 'phone', 'mail'] }}`
| `inline-check`| Provides a set of inlined checkbox components for selecting multiple options.
`argTypes: { contact: { control: 'inline-check', options: ['email', 'phone', 'mail'] }}`
| `select`| Provides a drop-down list component to handle single value selection. `argTypes: { age: { control: 'select', options: [20, 30, 40, 50] }}`
| `multi-select`| Provides a drop-down list that allows multiple selected values. `argTypes: { countries: { control: 'multi-select', options: ['USA', 'Canada', 'Mexico'] }}`
**string**| `text`| Provides a freeform text input.
`argTypes: { label: { control: 'text' }}`
| `color`| Provides a color picker component to handle color values.
Can be additionally configured to include a set of color presets.
`argTypes: { color: { control: { type: 'color', presetColors: ['red', 'green']} }}`
| `date`| Provides a datepicker component to handle date selection. `argTypes: { startDate: { control: 'date' }}`
💡
The `date` control will convert the date into a UNIX timestamp when the value changes. It's a known limitation that will be fixed in a future release. If you need to represent the actual date, you'll need to update the story's implementation and convert the value into a date object.
CSF 3CSF Next 🧪
Gizmo.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Gizmo } from './Gizmo';
const meta = {
component: Gizmo,
argTypes: {
canRotate: {
control: 'boolean',
},
width: {
control: { type: 'number', min: 400, max: 1200, step: 50 },
},
height: {
control: { type: 'range', min: 200, max: 1500, step: 50 },
},
rawData: {
control: 'object',
},
coordinates: {
control: 'object',
},
texture: {
control: {
type: 'file',
accept: '.png',
},
},
position: {
control: 'radio',
options: ['left', 'right', 'center'],
},
rotationAxis: {
control: 'check',
options: ['x', 'y', 'z'],
},
scaling: {
control: 'select',
options: [10, 50, 75, 100, 200],
},
label: {
control: 'text',
},
meshColors: {
control: {
type: 'color',
presetColors: ['#ff0000', '#00ff00', '#0000ff'],
},
},
revisionDate: {
control: 'date',
},
},
} satisfies Meta;
export default meta;
💡
Numeric data types will default to a `number` control unless additional configuration is provided.
###
Parameters
Controls supports the following configuration [parameters](../writing-stories/parameters), either globally or on a per-story basis:
####
Show full documentation for each property
Since Controls is built on the same engine as Storybook Docs, it can also show property documentation alongside your controls using the expanded parameter (defaults to false). This means you embed a complete [`Controls`](../api/doc-blocks/doc-block-controls) doc block in the controls panel. The description and default value rendering can be customized like the doc block.
To enable expanded mode globally, add the following to [`.storybook/preview.js|ts`](../configure/index#configure-story-rendering):
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
parameters: {
controls: { expanded: true },
},
};
export default preview;
Here's what the resulting UI looks like:

####
Specify initial preset color swatches
For `color` controls, you can specify an array of `presetColors`, either on the `control` in `argTypes`, or as a parameter under the `controls` namespace:
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
parameters: {
controls: {
presetColors: [{ color: '#ff4785', title: 'Coral' }, 'rgba(0, 159, 183, 1)', '#fe4a49'],
},
},
};
export default preview;
Color presets can be defined as an object with `color` and `title` or a simple CSS color string. These will then be available as swatches in the color picker. When you hover over the color swatch, you'll be able to see its title. It will default to the nearest CSS color name if none is specified.
####
Filtering controls
In specific cases, you may be required to display only a limited number of controls in the controls panel or all except a particular set.
To make this possible, you can use optional `include` and `exclude` configuration fields in the `controls` parameter, which you can define as an array of strings or a regular expression.
Consider the following story snippets:
CSF 3CSF Next 🧪
YourComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { YourComponent } from './YourComponent';
const meta = {
component: YourComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const ArrayInclude: Story = {
parameters: {
controls: { include: ['foo', 'bar'] },
},
};
export const RegexInclude: Story = {
parameters: {
controls: { include: /^hello*/ },
},
};
export const ArrayExclude: Story = {
parameters: {
controls: { exclude: ['foo', 'bar'] },
},
};
export const RegexExclude: Story = {
parameters: {
controls: { exclude: /^hello*/ },
},
};
####
Sorting controls
By default, controls are unsorted and use whatever order the args data is processed in (`none`). Additionally, you can sort them alphabetically by the arg's name (`alpha`) or with the required args first (`requiredFirst`).
Consider the following snippet to force required args first:
CSF 3CSF Next 🧪
YourComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { YourComponent } from './YourComponent';
const meta = {
component: YourComponent,
parameters: { controls: { sort: 'requiredFirst' } },
} satisfies Meta;
export default meta;
###
Disable controls for specific properties
Aside from the features already documented here, Controls can also be disabled for individual properties.
Suppose you want to turn off Controls for a property called `foo` in a component's story. The following example illustrates how:
CSF 3CSF Next 🧪
YourComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { YourComponent } from './YourComponent';
const meta = {
component: YourComponent,
argTypes: {
// foo is the property we want to remove from the UI
foo: {
table: {
disable: true,
},
},
},
} satisfies Meta;
export default meta;
Resulting in the following change in Storybook UI:
The previous example also removed the prop documentation from the table. In some cases, this is fine. However, sometimes you might want to render the prop documentation without a control. The following example illustrates how:
CSF 3CSF Next 🧪
YourComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { YourComponent } from './YourComponent';
const meta = {
component: YourComponent,
argTypes: {
// foo is the property we want to remove from the UI
foo: {
control: false,
},
},
} satisfies Meta;
export default meta;
💡
As with other Storybook properties, such as [decorators](../writing-stories/decorators), you can apply the same pattern at a story level for more granular cases.
###
Conditional controls
In some cases, it's useful to be able to conditionally exclude a control based on the value of another control. Controls supports basic versions of these use cases with the `if`, which can take a simple query object to determine whether to include the control.
Consider a collection of "advanced" settings only visible when the user toggles an "advanced" toggle.
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
argTypes: {
label: { control: 'text' }, // Always shows the control
advanced: { control: 'boolean' },
// Only enabled if advanced is true
margin: { control: 'number', if: { arg: 'advanced' } },
padding: { control: 'number', if: { arg: 'advanced' } },
cornerRadius: { control: 'number', if: { arg: 'advanced' } },
},
} satisfies Meta;
export default meta;
Or consider a constraint where if the user sets one control value, it doesn't make sense for the user to be able to set another value.
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Meta } from '@storybook/your-framework';
import { Button } from './Button';
const meta = {
component: Button,
argTypes: {
// Button can be passed a label or an image, not both
label: {
control: 'text',
if: { arg: 'image', truthy: false },
},
image: {
control: { type: 'select', options: ['foo.jpg', 'bar.jpg'] },
if: { arg: 'label', truthy: false },
},
},
} satisfies Meta;
export default meta;
The query object must contain either an `arg` or `global` target:
field| type| meaning
---|---|---
arg| string| The ID of the arg to test.
global| string| The ID of the global to test.
It may also contain at most one of the following operators:
operator| type| meaning
---|---|---
truthy| boolean| Is the target value truthy?
exists| boolean| Is the target value defined?
eq| any| Is the target value equal to the provided value?
neq| any| Is the target value NOT equal to the provided value?
If no operator is provided, that is equivalent to `{ truthy: true }`.
##
Troubleshooting
###
The controls are not updating the story within the auto-generated documentation
If you turned off inline rendering for your stories via the [`inline`](../api/doc-blocks/doc-block-story#inline) configuration option, you would run into a situation where the associated controls are not updating the story within the documentation page. This is a known limitation of the current implementation and will be addressed in a future release.
##
API
###
Parameters
This feature contributes the following [parameters](../writing-stories/parameters) to Storybook, under the `controls` namespace:
####
`disable`
Type: `boolean`
Disable this feature's behavior. If you wish to disable this feature for the entire Storybook, you should [do so in your main configuration file](./index#disabling-features).
This parameter is most useful to allow overriding at more specific levels. For example, if this parameter is set to `true` at the project level, it could then be re-enabled by setting it to `false` at the meta (component) or story level.
####
`exclude`
Type: `string[] | RegExp`
Specifies which properties to exclude from the Controls panel. Any properties whose names match the regex or are part of the array will be left out. See usage example, above.
####
`expanded`
Type: `boolean`
Show the full documentation for each property in the Controls panel, including the description and default value. See usage example, above.
####
`include`
Type: `string[] | RegExp`
Specifies which properties to include in the Controls panel. Any properties whose names don't match the regex or are not part of the array will be left out. See usage example, above.
####
`presetColors`
Type: `(string | { color: string; title?: string })[]`
Specify preset color swatches for the color picker control. The color value may be any valid CSS color. See usage example, above.
####
`sort`
Type: `'none' | 'alpha' | 'requiredFirst'`
Default: `'none'`
Specifies how the controls are sorted.
* **none** : Unsorted, displayed in the same order the arg types are processed in
* **alpha** : Sorted alphabetically, by the arg type's name
* **requiredFirst** : Same as `alpha`, with any required arg types displayed first
####
`disableSaveFromUI`
Type: `boolean`
Default: `false`
Disable the ability to create or edit stories from the Controls panel.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/essentials/controls.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/essentials/highlight
# Page: /docs/essentials/highlight
# Highlight
ReactVueAngularWeb ComponentsMore
Storybook's Highlight feature is a helpful tool for visually debugging your components. It allows you to highlight specific DOM nodes within your story when used directly or enhancing addons such as the [Accessibility addon](../writing-tests/accessibility-testing) to inform you of accessibility issues within your components.

##
Highlighting DOM Elements
To highlight DOM elements with the feature, you'll need to emit the `HIGHLIGHT` event from within a story or an addon. The event payload must contain a `selectors` property assigned to an array of selectors matching the elements you want to highlight. For example:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const Highlighted: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(HIGHLIGHT, {
selectors: ['h2', 'a', '.storybook-button'],
});
return storyFn();
},
],
};
💡
We recommend choosing the most specific selector possible to avoid highlighting elements other addons use. This is because the feature tries to match selectors against the entire DOM tree.
###
Customize style
By default, highlighted elements contain a standard outline style applied to the selected elements. However, you can enable your custom style by extending the payload object with additional properties to customize the appearance of the highlighted elements. For example:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const StyledHighlight: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(HIGHLIGHT, {
selectors: ['h2', 'a', '.storybook-button'],
styles: {
backgroundColor: `color-mix(in srgb, hotpink, transparent 90%)`,
outline: '3px solid hotpink',
animation: 'pulse 3s linear infinite',
transition: 'outline-offset 0.2s ease-in-out',
},
hoverStyles: {
outlineOffset: '3px',
},
focusStyles: {
backgroundColor: 'transparent',
},
keyframes: `@keyframes pulse {
0% { outline-color: rgba(255, 105, 180, 1); }
50% { outline-color: rgba(255, 105, 180, 0.2); }
100% { outline-color: rgba(255, 105, 180, 1); }
}`,
});
return storyFn();
},
],
};
ℹ️
These properties are optional, and you can use them to customize the appearance of the highlighted elements. The `hoverStyles` and `focusStyles` properties are recommended for use with the `menu` property. Pseudo-classes and pseudo-elements are not supported.
###
Highlight menu
The Highlight feature includes a built-in debugging option, allowing you to select the highlighted elements when you click them. This is particularly useful for inspecting the elements affected by the feature, as it lets you preview a list of elements matching the selector you provided. To enable it, add a `menu` property in the payload object containing additional information about the elements or trigger actions. Each item must include an `id` and a `title`, and you can also provide an optional `selectors` property to limit the menu item to specific highlighted elements.

CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const StyledHighlight: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(HIGHLIGHT, {
selectors: ['h2', 'a', '.storybook-button'],
menu: [
[
{
id: 'button-name',
title: 'Login',
description: 'Navigate to the login page',
clickEvent: 'my-menu-click-event',
},
{
id: 'h2-home',
title: 'Acme',
description: 'Navigate to the home page',
},
],
],
});
return storyFn();
},
],
};
When enabled, the menu will be displayed when you click on the selected element matching your provided selectors. However, if you don't want to show any information, you can omit the items or set the `menu` property to an empty array to show the default menu.

##
Remove highlights
If you need to remove a highlight from a specific element, you can do so by emitting the `REMOVE_HIGHLIGHT` event and providing the `id` of the highlight you want to remove. For example:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT, REMOVE_HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const RemoveHighlight: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(HIGHLIGHT, {
id: 'my-unique-id',
selectors: ['header', 'section', 'footer'],
});
emit(REMOVE_HIGHLIGHT, 'my-unique-id');
return storyFn();
},
],
};
ℹ️
The `emit` function derived from the `useChannel` API hook creates a communication channel in Storybook's UI to listen for events and update the UI accordingly. The Highlight feature uses this channel to listen to custom events and update the highlighted elements (if any) accordingly.
##
Reset highlighted elements
Out of the box, Storybook automatically removes highlighted elements when transitioning between stories. However, if you need to clear them manually, you can emit the `RESET_HIGHLIGHT` event from within a story or an addon. This removes all highlights, even ones created by other addons. For example:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { HIGHLIGHT, RESET_HIGHLIGHT } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const ResetHighlight: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(RESET_HIGHLIGHT); //👈 Remove previously highlighted elements
emit(HIGHLIGHT, {
selectors: ['header', 'section', 'footer'],
});
return storyFn();
},
],
};
##
Scroll element into view
The Highlight feature allows you to scroll an element into view and highlight it. To enable it, emit the `SCROLL_INTO_VIEW` event from within a story or an addon. The event payload must contain a `selector` property to target the element you want to scroll into view. When the element is visible, it will be highlighted for a brief moment.
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { useChannel } from 'storybook/preview-api';
import { SCROLL_INTO_VIEW } from 'storybook/highlight';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const ScrollIntoView: Story = {
decorators: [
(storyFn) => {
const emit = useChannel({});
emit(SCROLL_INTO_VIEW, '#footer');
return storyFn();
},
],
};
##
API
###
Parameters
This feature contributes the following [parameters](../writing-stories/parameters) to Storybook, under the `highlight` namespace:
####
`disable`
Type: `boolean`
Disable this feature's behavior. If you wish to turn off this feature for the entire Storybook, you should [do so in your main configuration file](./index#disabling-features).
This parameter is most useful to allow overriding at more specific levels. For example, if this parameter is set to `true` at the project level, it could be re-enabled by setting it to `false` at the meta (component) or story level.
###
Exports
This feature contributes the following exports to Storybook:
import { HIGHLIGHT, REMOVE_HIGHLIGHT, RESET_HIGHLIGHT, SCROLL_INTO_VIEW } from 'storybook/highlight';
####
`HIGHLIGHT`
An event to highlight DOM elements. The event payload must contain a `selectors` property assigned to an array of selectors matching the elements you want to highlight. It can be extended with an optional object containing additional configuration options. See the usage example above.
import { HIGHLIGHT, type HighlightOptions } from 'storybook/highlight';
channel.emit(
HIGHLIGHT,
options // The available configuration options inheriting from the HighlightOptions API
);
The `options` object contains the following properties:
interface HighlightOptions {
/** Unique identifier for the highlight, required if you want to remove the highlight later */
id?: string;
/** HTML selectors of the elements */
selectors: string[];
/** Priority of the highlight, higher takes precedence, defaults to 0 */
priority?: number;
/** CSS styles to apply to the highlight */
styles?: Record;
/** CSS styles to apply to the highlight when it is hovered */
hoverStyles?: Record;
/** CSS styles to apply to the highlight when it is focused or selected */
focusStyles?: Record;
/** Keyframes required for animations */
keyframes?: string;
/** Groups of menu items to show when the highlight is selected */
menu?: HighlightMenuItem[][];
}
interface HighlightMenuItem {
/** Unique identifier for the menu item */
id: string;
/** Title of the menu item */
title: string;
/** Description of the menu item */
description?: string;
/** Icon for the menu item, left side */
iconLeft?: "chevronLeft" | "chevronRight" | "info" | "shareAlt";
/** Icon for the menu item, right side */
iconRight?: "chevronLeft" | "chevronRight" | "info" | "shareAlt";
/** Name for a channel event to trigger when the menu item is clicked */
clickEvent?: string;
/** HTML selectors for which this menu item should show (subset of HighlightOptions['selectors']) */
selectors?: HighlightOptions['selectors'];
}
Menu items can specify a `clickEvent` to be emitted on the channel when the item is clicked. The channel event will receive two arguments: the menu item `id` and a `ClickEventDetails` object with the following properties:
interface ClickEventDetails {
// Position and dimensions of the element on the page
top: number;
left: number;
width: number;
height: number;
// Selector(s) which matched the element
selectors: string[];
// DOM element details
element: {
attributes: Record;
localName: string;
tagName: string;
outerHTML: string;
};
}
To listen for this event (assuming `clickEvent: 'MY_CLICK_EVENT'`):
import type { ClickEventDetails } from 'storybook/highlight';
const handleClickEvent = (itemId: string, details: ClickEventDetails) => {
// Handle the menu item click event
}
// When you have a channel instance:
channel.on('MY_CLICK_EVENT', handleClickEvent)
// Or from a decorator:
useChannel({
MY_CLICK_EVENT: handleClickEvent,
}, [handleClickEvent])
####
`REMOVE_HIGHLIGHT`
An event that removes a previously created highlight. The event payload must contain an `id` property assigned to the id of the highlight you want to remove. See the usage example above.
import { REMOVE_HIGHLIGHT } from 'storybook/highlight';
channel.emit(
REMOVE_HIGHLIGHT,
id // The id of the previously created highlight to be removed
);
####
`RESET_HIGHLIGHT`
An event to clear all highlights from highlighted elements. See the usage example above.
import { RESET_HIGHLIGHT } from 'storybook/highlight';
channel.emit(RESET_HIGHLIGHT);
####
`SCROLL_INTO_VIEW`
An event to scroll a DOM element into view and briefly highlight it. The event payload must contain a selector property assigned to the selector of the element you want to scroll into view. Optionally, you can provide a [`options`](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView#scrollintoviewoptions) object to customize the scroll behavior. See the usage example above.
import { SCROLL_INTO_VIEW } from 'storybook/highlight';
channel.emit(
SCROLL_INTO_VIEW,
selector // Element selector to scroll into view
options // An object inheriting from ScrollIntoViewOptions API to customize the scroll behavior
);
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/essentials/highlight.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/essentials/measure-and-outline
# Page: /docs/essentials/measure-and-outline
# Measure & outline
ReactVueAngularWeb ComponentsMore
Storybook's measure and outline features give you the necessary tooling to inspect and visually debug CSS layout and alignment issues within your stories. It makes it easy to catch UI bugs early in development.
##
Measure
While working with composite components or page layouts, dealing with whitespace (i.e., `margin`, `padding`, `border`) and individual component measurements can be tedious. It would require that you open up the browser's development tools and manually inspect the DOM tree for issues and UI bugs.
Instead, you can quickly visualize each component's measurements by clicking the measure button in the toolbar. Now when you hover over an element in your story, that element's dimensions and any whitespace (i.e., `margin`, `padding`, `border`) will be shown.

💡
Alternatively you can press the `m` key on your keyboard to toggle measure on and off.
##
Outline
When building your layouts, checking the visual alignment of all components can be pretty complicated, especially if your components are spread apart or contain unique shapes.
Click the outline button in the toolbar to toggle the outlines associated with all your UI elements, allowing you to spot bugs and broken layouts instantly.

##
API
###
Parameters
These features contribute the following [parameters](../writing-stories/parameters) to Storybook, under the `measure` or `outline` namespace:
####
`disable`
Type: `boolean`
Disable the feature's behavior. If you wish to disable the feature for the entire Storybook, you should [do so in your main configuration file](./index#disabling-features).
This parameter is most useful to allow overriding at more specific levels. For example, if this parameter is set to `true` at the project level, it could then be re-enabled by setting it to `false` at the meta (component) or story level.
Was this page useful?
👍👎
[✍️ Edit on Github](https://github.com/storybookjs/storybook/tree/next/docs/essentials/measure-and-outline.mdx)
---
# Storybook Documentation
# Source: https://storybook.js.org/docs/essentials/toolbars-and-globals
# Page: /docs/essentials/toolbars-and-globals
# Toolbars & globals
ReactVueAngularWeb ComponentsMore
Storybook ships with features to control the [viewport](./viewport) and [background](./backgrounds) the story renders in. Similarly, you can use built-in features to create toolbar items which control special “globals”. You can then read the global values to create [decorators](../writing-stories/decorators) to control story rendering.

##
Globals
Globals in Storybook represent “global” (as in not story-specific) inputs to the rendering of the story. As they aren’t specific to the story, they aren’t passed in the `args` argument to the story function (although they are accessible as `context.globals`). Instead, they are typically used in decorators, which apply to all stories.
When the globals change, the story re-renders and the decorators rerun with the new values. The easiest way to change globals is to create a toolbar item for them.
##
Global types and the toolbar annotation
Storybook has a simple, declarative syntax for configuring toolbar menus. In your [`.storybook/preview.js|ts`](../configure/index#configure-story-rendering), you can add your own toolbars by creating `globalTypes` with a `toolbar` annotation:
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
globalTypes: {
theme: {
description: 'Global theme for components',
toolbar: {
// The label to show for this toolbar item
title: 'Theme',
icon: 'circlehollow',
// Array of plain string values or MenuItem shape (see below)
items: ['light', 'dark'],
// Change title based on selected value
dynamicTitle: true,
},
},
},
initialGlobals: {
theme: 'light',
},
};
export default preview;
💡
As globals are _global_ you can _only_ set `globalTypes` and `initialGlobals` in [`.storybook/preview.js|ts`](../configure/index#configure-story-rendering).
When you start your Storybook, your toolbar should have a new dropdown menu with the `light` and `dark` options.
##
Create a decorator
We have a `global` implemented. Let's wire it up! We can consume our new `theme` global in a decorator using the `context.globals.theme` value.
For example, suppose you are using [`styled-components`](https://styled-components.com/). You can add a theme provider decorator to your [`.storybook/preview.js|ts`](../configure/index#configure-story-rendering) config:
CSF 3CSF Next 🧪
.storybook/preview.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Preview } from '@storybook/your-framework';
import { ThemeProvider } from 'styled-components';
import { MyThemes } from '../my-theme-folder/my-theme-file';
const preview: Preview = {
decorators: [
(Story, context) => {
const theme = MyThemes[context.globals.theme];
return (
);
},
],
};
export default preview;
##
Setting globals on a story
When a global value is changed with a toolbar menu in Storybook, that value continues to be used as you navigate between stories. But sometimes a story requires a specific value to render correctly, e.g., when testing against a particular environment.
To ensure that a story always uses a specific global value, regardless of what has been chosen in the toolbar, you can set the `globals` annotation on a story or component. This overrides the global value for those stories and disables the toolbar menu for that global when viewing the stories.
CSF 3CSF Next 🧪
Button.stories.ts|tsx
Typescript
// 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 { Button } from './Button';
const meta = {
component: Button,
globals: {
// 👇 Set background value for all component stories
backgrounds: { value: 'gray', grid: false },
},
} satisfies Meta;
export default meta;
type Story = StoryObj;
export const OnDark: Story = {
globals: {
// 👇 Override background value for this story
backgrounds: { value: 'dark' },
},
};
In the example above, Storybook will force all Button stories to use a gray background color, except the `OnDark` story, which will use the dark background. For all Button stories, the toolbar menu will be disabled for the `backgrounds` global, with a tooltip explaining that the global is set at the story level.
💡
Configuring a story's `globals` annotation to override the project-level global settings is useful but should be used with moderation. Globals that are _not_ defined at the story level can be selected interactively in Storybook's UI, allowing users to explore every existing combination of values (e.g., global values, [`args`](../writing-stories/args)). Setting them at the story level will disable that control, preventing users from exploring the available options.
##
Advanced usage
So far, we've created and used a global inside Storybook.
Now, let's take a look at a more complex example. Suppose we wanted to implement a new global called **locale** for internationalization, which shows a flag on the right side of the toolbar.
In your [`.storybook/preview.js|ts`](../configure/index#configure-story-rendering), add the following:
CSF 3CSF Next 🧪
.storybook/preview.ts
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { Preview } from '@storybook/your-framework';
const preview: Preview = {
globalTypes: {
locale: {
description: 'Internationalization locale',
toolbar: {
icon: 'globe',
items: [
{ value: 'en', right: '🇺🇸', title: 'English' },
{ value: 'fr', right: '🇫🇷', title: 'Français' },
{ value: 'es', right: '🇪🇸', title: 'Español' },
{ value: 'zh', right: '🇨🇳', title: '中文' },
{ value: 'kr', right: '🇰🇷', title: '한국어' },
],
},
},
},
initialGlobals: {
locale: 'en',
},
};
export default preview;
💡
The `icon` element used in the examples loads the icons from the `@storybook/icons` package. See [here](../faq#what-icons-are-available-for-my-toolbar-or-my-addon) for the list of available icons that you can use.
Adding the configuration element `right` will display the text on the right side in the toolbar menu once you connect it to a decorator.
Here's a list of the available configuration options.
MenuItem| Type| Description| Required
---|---|---|---
**value**| String| The string value of the menu that gets set in the globals| Yes
**title**| String| The main text of the title| Yes
**right**| String| A string that gets displayed on the right side of the menu| No
**icon**| String| An icon that gets shown in the toolbar if this item is selected| No
##
Consuming globals from within a story
We recommend consuming globals from within a decorator and defining a global setting for all stories.
But we're aware that sometimes it's more beneficial to use toolbar options on a per-story basis.
Using the example above, you can modify any story to retrieve the **Locale** `global` from the story context:
CSF 3CSF Next 🧪
MyComponent.stories.ts|tsx
Typescript
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
import { MyComponent } from './MyComponent';
const meta = {
component: MyComponent,
} satisfies Meta;
export default meta;
type Story = StoryObj;
const getCaptionForLocale = (locale) => {
switch (locale) {
case 'es':
return 'Hola!';
case 'fr':
return 'Bonjour!';
case 'kr':
return '안녕하세요!';
case 'zh':
return '你好!';
default:
return 'Hello!';
}
};
export const StoryWithLocale = {
render: (args, { globals: { locale } }) => {
const caption = getCaptionForLocale(locale);
return