Docs
Storybook Docs

TypeScript

Storybook provides an integrated TypeScript experience, including zero-configuration setup and built-in types for APIs, addons, and stories.

Storybook requires TypeScript 5.9 or newer.

TypeScript 7

TypeScript 7 does not include the compiler API that Storybook uses to read your component types for Controls and the Docs args table. Other tools that import typescript, such as typescript-eslint, need that API too.

Install TypeScript 7 and TypeScript 6 side by side, using the setup from the TypeScript team. tsc stays on TypeScript 7. The package named typescript, which Storybook imports, is TypeScript 6. npm, Yarn, and pnpm all accept this alias:

{
  "devDependencies": {
    "@typescript/native": "npm:typescript@^7.0.2",
    "typescript": "npm:@typescript/typescript6@^6.0.2"
  }
}

npx tsc then runs TypeScript 7. If you replace typescript and leave out @typescript/native, the TypeScript 7 tsc binary is gone and only tsc6 remains.

Your editor uses the package named typescript by default, so its language service stays on TypeScript 6 until you select @typescript/native.

If your project depends on TypeScript 7 directly, including a new Vite app, replace that typescript dependency with the two entries above.

Until you do, React projects use the deprecated builder docgen with react-docgen. Storybook adds it to new and upgraded projects on TypeScript 7.0 for now, and offers to remove it again once it supports TypeScript 7.1.

Configure Storybook with TypeScript

Storybook's configuration file (i.e., main.ts) is defined as an ESM module written in TypeScript, providing you with the baseline configuration to support your existing framework while enabling you stricter type-checking and autocompletion in your editor. Below is an abridged configuration file.

.storybook/main.ts
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';
 
export default defineMain({
  // Required
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  // Optional
  addons: ['@storybook/addon-docs'],
  staticDirs: ['../public'],
});

See the main configuration API reference for more details and additional properties.

See the Vite builder TypeScript documentation if using @storybook/builder-vite.

Extending the default configuration

Out of the box, Storybook uses server-side docgen to infer component metadata. If you turn docgenServer off, you can use the legacy builder-based react-docgen or react-docgen-typescript parser. The options below configure that legacy path.

OptionDescription
checkAvailable for Webpack-based projects.
Enables type checking within Storybook
typescript: { check: true },
checkOptionsRequires the check option to be enabled.
Configures the fork-ts-checker-webpack-plugin plugin
typescript: { checkOptions: {},},
reactDocgenConfigures the legacy TypeScript parser when docgenServer is off.
Available options: react-docgen (builder default), react-docgen-typescript, false
typescript: { reactDocgen: 'react-docgen' },
reactDocgenTypescriptOptionsRequires reactDocgen: 'react-docgen-typescript' and docgenServer: false.
Configures the react-docgen-typescript-plugin for the builder.
typescript: { reactDocgen: 'react-docgen-typescript', reactDocgenTypescriptOptions: {} },
skipCompilerDisables parsing Typescript files through the compiler
typescript: { skipCompiler:false,},
.storybook/main.ts
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';
 
export default defineMain({
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  typescript: {
    check: false,
    checkOptions: {},
    skipCompiler: false,
  },
});

Additional options are available for the typescript configuration option. See the config.typescript API reference for more information.

Write stories with TypeScript

Storybook provides zero-config TypeScript support, allowing you to write stories using this language without additional configuration. You can use this format for improved type safety and code completion. For example, if you're testing a Button component, you could do the following in your story file:

Button.stories.ts|tsx
import preview from '../.storybook/preview';
 
import { Button } from './Button';
 
const meta = preview.meta({
  component: Button,
});
 
//๐Ÿ‘‡ Throws a type error if the args don't match the component props
export const Primary = meta.story({
  args: {
    primary: true,
  },
});

The example above uses the power of TypeScript in combination with the exported generic types (Meta and StoryObj) to tell Storybook how to infer the component's metadata and the type of the component's inputs (e.g., props). This can greatly improve the developer experience by letting your IDE show you what properties are injected by Storybook.

Stricter checking with satisfies

You can use the satisfies operator to apply stricter type checking to your component stories. For example:

Button.stories.ts|tsx
// 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,
} satisfies Meta<typeof Button>; // ๐Ÿ‘ˆ Satisfies operator being used for stricter type checking.
 
export default meta;

Now, when you define a story or update an existing one, you'll automatically get notified that you're missing a required arg. However, you're not limited to using the satisfies operator at the component level. If you need, you can also use it at the story level. For example:

Button.stories.ts|tsx
// 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<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Example = {
  args: {
    primary: true,
    label: 'Button',
  },
} satisfies Story;

Troubleshooting

The satisfies operator is not working as expected

Storybook supports the satisfies operator for almost every supported framework. However, Angular and Web Components users might run into issues when applying this operator for additional type safety. Their renderer types cannot always determine whether a component property is required. If you encounter this issue, please open a support request on GitHub Discussions.

Storybook doesn't create the required types for external packages

If your project relies on a third-party library and the server does not generate the expected types, you can opt out of server-side docgen and use react-docgen-typescript with the required options. Install its packages first: @joshwooding/vite-plugin-react-docgen-typescript and react-docgen-typescript with Vite, or @storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0 (this exact version; the latest tag is older) and react-docgen-typescript with Webpack. For example:

.storybook/main.ts
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';
 
export default defineMain({
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  features: {
    docgenServer: false,
  },
  typescript: {
    reactDocgen: 'react-docgen-typescript',
    reactDocgenTypescriptOptions: {
      compilerOptions: {
        allowSyntheticDefaultImports: false,
        esModuleInterop: false,
      },
      // Filter out third-party props from node_modules except @mui packages.
      propFilter: (prop) =>
        prop.parent ? !/node_modules\/(?!@mui)/.test(prop.parent.fileName) : true,
    },
  },
});

Inherited args are missing for components from workspace packages

If you've opted out of server-side docgen and use react-docgen-typescript in a monorepo with npm/yarn/pnpm workspaces, you may find that components imported from a workspace package are missing inherited args (e.g., MUI's ButtonProps), while the same component imported locally works fine.

This happens because the underlying Vite plugin creates a TypeScript program from files matching its include glob (default: **/**.tsx), resolved from the Storybook project's directory. Workspace package files are outside that directory and aren't included in the program, so inherited types can't be resolved.

To fix this, add your workspace package source files to the include option:

.storybook/main.ts
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';
 
export default defineMain({
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  features: {
    docgenServer: false,
  },
  typescript: {
    reactDocgen: 'react-docgen-typescript',
    reactDocgenTypescriptOptions: {
      // ๐Ÿ‘‡ Add your workspace package source files so they're included in the TS program
      include: ['**/*.tsx', '../../packages/ui/src/**/*.tsx'],
    },
  },
});

Adjust the path (../../packages/ui/src/**/*.tsx) to match your monorepo layout. You might expect that pointing tsconfigPath to a tsconfig that includes your workspace packages would solve this, but tsconfigPath only affects which compiler options are used. It does not change which files are included in the TypeScript program.

The types are not being generated for my component

If server-side docgen does not generate the expected types, you can use the legacy react-docgen-typescript path instead. This requires features.docgenServer: false and the packages listed above; the example includes the opt-out:

.storybook/main.ts
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';
 
export default defineMain({
  framework: '@storybook/your-framework',
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  features: {
    docgenServer: false,
  },
  typescript: {
    reactDocgen: 'react-docgen-typescript',
    // Provide your own options if necessary.
    // See https://storybook.js.org/docs/configure/typescript for more information.
    reactDocgenTypescriptOptions: {},
  },
});

If you're still encountering issues, we recommend reaching out to the community using the default communication channels (e.g., GitHub discussions).