TypeScript Config

Edit this page

This is a central reference for using Storybook with TypeScript.

Typescript configuration presets

The fastest and easiest way to write and configure your stories in TypeScript is by using a Storybook preset.

  • If you’re using Create React App (CRA) and have configured it to work with TS, you should use @storybook/preset-create-react-app, which configures Storybook to reuse CRA’s TS handling.

  • If you are not using CRA or have other requirements, then the next best option is @storybook/preset-typescript, which configures ts-loader under the hood.

If you need more control than these presets offer, read on for manual configuration instructions.

You can learn more about Storybook presets over here.

If using TypeScript, some addons require features available in TS version 3.4+.

Setting up TypeScript with ts-loader

Dependencies you may need

yarn add -D typescript
yarn add -D ts-loader
yarn add -D @storybook/addon-info react-docgen-typescript-loader # optional but recommended
yarn add -D jest "@types/jest" ts-jest #testing

Setting up TypeScript to work with Storybook

We configure storybook’s webpack by changing .storybook/main.js:

module.exports = {
webpackFinal: async config => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    use: [
      {
        loader: require.resolve('ts-loader'),
      },
      // Optional
      {
        loader: require.resolve('react-docgen-typescript-loader'),
      },
    ],
  });
  config.resolve.extensions.push('.ts', '.tsx');
  return config;
},
};

The above example shows a working Webpack config with the TSDocgen plugin integrated. This plugin is not necessary to use Storybook and the section marked // optional can be safely removed if the features of TSDocgen are not required.

tsconfig.json

{
"compilerOptions": {
  "outDir": "build/lib",
  "module": "commonjs",
  "target": "es5",
  "lib": ["es5", "es6", "es7", "es2017", "dom"],
  "sourceMap": true,
  "allowJs": false,
  "jsx": "react",
  "moduleResolution": "node",
  "rootDirs": ["src", "stories"],
  "baseUrl": "src",
  "forceConsistentCasingInFileNames": true,
  "noImplicitReturns": true,
  "noImplicitThis": true,
  "noImplicitAny": true,
  "strictNullChecks": true,
  "suppressImplicitAnyIndexErrors": true,
  "noUnusedLocals": true,
  "declaration": true,
  "allowSyntheticDefaultImports": true,
  "experimentalDecorators": true,
  "emitDecoratorMetadata": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build", "scripts"]
}

This is for the default configuration where /stories is a peer of src. If you have them all in src, you may wish to replace "rootDirs": ["src", "stories"] above with "rootDir": "src",.

Setting up TypeScript with babel-loader

A note for Create React App users

Please use @storybook/preset-create-react-app for full compatibility with Create React App features - which includes TypeScript support.

Setting up TypeScript to work with Storybook

The following code uses babel-preset-react-app.

We will create a custom Webpack config by creating editing/creating the .storybook/main.js:

module.exports = {
stories: ['../src/**/*.stories.tsx'],
webpackFinal: async config => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    loader: require.resolve('babel-loader'),
    options: {
      presets: [['react-app', { flow: false, typescript: true }]],
    },
  });
  config.resolve.extensions.push('.ts', '.tsx');
  return config;
},
};

tsconfig.json

If your stories are outside the src folder, for example the stories folder in root, then "rootDirs": ["src", "stories"] needs to be added to be added to compilerOptions so it knows what folders to compile. Make sure jsx is set to preserve. Should be unchanged.

Create a TSX storybook index

The default storybook index file is stories/index.stories.js — you’ll want to rename this to stories/index.stories.tsx.

Using TypeScript with the TSDocgen addon

The very handy Storybook Info addon autogenerates prop tables documentation for each component, however it doesn’t work with Typescript types. The current solution is to use react-docgen-typescript-loader to preprocess the TypeScript files to give the Info addon what it needs. The webpack config above does this, and so for the rest of your stories you use it as per normal:

import * as React from 'react';
import TicTacToeCell from './TicTacToeCell';

export default {
  title: 'Components',
  parameters: {
    info: { inline: true },
  },
};

export const TicTacToeCell = () => (
  <TicTacToeCell value="X" position={{ x: 0, y: 0 }} />,
);

Customizing Type annotations/descriptions

Please refer to the react-docgen-typescript-loader docs for writing prop descriptions and other annotations to your Typescript interfaces.

Additional annotation can be achieved by setting a default set of info parameters in .storybook/preview.js:

import { addDecorator } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';

addDecorator(
  withInfo({
    styles: {
      header: {
        h1: {
          marginRight: '20px',
          fontSize: '25px',
          display: 'inline',
        },
        body: {
          paddingTop: 0,
          paddingBottom: 0,
        },
        h2: {
          display: 'inline',
          color: '#999',
        },
      },
      infoBody: {
        backgroundColor: '#eee',
        padding: '0px 5px',
        lineHeight: '2',
      },
    },
    inline: true,
    source: false,
  })
);

Note: Component docgen information can not be generated for components that are only exported as default. You can work around the issue by exporting the component using a named export.

Setting up Jest tests

The ts-jest README explains pretty clearly how to get Jest to recognize TypeScript code.

This is an example jest.config.js file for jest:

module.exports = {
transform: {
  '.(ts|tsx)': 'ts-jest',
},
testPathIgnorePatterns: ['/node_modules/', '/lib/'],
testRegex: '(/test/.*|\\.(test|spec))\\.(ts|tsx|js)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
};

Building your TypeScript Storybook

You will need to set up some scripts - these may help:

"scripts": {
"start": "react-scripts-ts start",
"build": "npm run lint && npm run build-lib && build-storybook",
"build-lib-watch": "tsc -w",
"build-lib": "tsc && npm run copy-css-to-lib && npm run copy-svg-to-lib && npm run copy-png-to-lib && npm run copy-woff2-to-lib",
"test": "react-scripts-ts test --env=jsdom",
"test:coverage": "npm test -- --coverage",
"eject": "react-scripts-ts eject",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook",
"copy-css-to-lib": "cpx \"./src/**/*.css\" ./build/lib",
"copy-woff2-to-lib": "cpx \"./src/**/*.woff2\" ./build/lib",
"copy-svg-to-lib": "cpx \"./src/**/*.svg\" ./build/lib",
"copy-png-to-lib": "cpx \"./src/**/*.png\" ./build/lib",
"lint": "tslint -c tslint.json 'src/**/*.{ts,tsx}'"
  },