How to set up Storybook v6 with React 17

coded structure
HI
By Harrison Ifeanyichukwu

Storybook has become the de facto standard for building UI components. Storybook is a great tool for iterating on UI components. It supports the most popular UI frameworks out there, including React. Here is a guide on how to setup Storybook v6 with React version 17.

Storybook provides the ability to test UI components in isolation. With Storybook, UI components can be iterated on and documented as they are developed.

Because Storybook runs in isolation from the project, it can be hosted and shared with design teams and stakeholders for reviews. This improves the team's feedback speed and overall development speed.

Storybook's latest version, version 6 has added many features on top of the previous version 5 while keeping both compatible. Version 6 simplified many things, including how to define Webpack configurations, Babel configurations, addons integration and storybook parameters.

React 17

React v17, the latest React release, is not much different from the previous version 16; in fact, according to the React team, React 17 does not add any new features but rather focused and improved the library's toolchain, in what it termed preparing for future releases.

One of the updates in React 17 is the ability to code in JSX without the need for React global import. With previous React versions, you must import the React global in any js file that has JSX codes as it is needed for creating elements as shown below.

// working with React versions before version 17
import React from 'react';

export const HelloComponent = () => {
    return (
        return <div>Hello World</div>
    )
}

With React version 17, JSX transformation got improved and lifted away from the React package. Importing the React global within your JSX file is no longer necessary.

The React team worked along with the Babel team to add support to the Babel JSX transform plugin, which changed the game.

//React versions 17+, React import not needed.
export const HelloComponent = () => {
    return (
        return <div>Hello World</div>
    )
}

According to React, this new syntax will bring performance improvements and can reduce the overall bundle size. Unfortunately, Storybook version 6 does not support React version 17 out of the box when React global is not imported.

Setting up Storybook v6 with React v17

Below are the steps to take when setting up storybook v6 with React v17.

  1. Upgrade to React version 17
  2. Upgrade to storybook version 6
  3. Upgrade typescript to support new JSX syntax
  4. Modify Storybook config to support React version 17

We will go into details for each step.

1. Upgrade to React version 17

The first step is to upgrade to version 17 of React. To upgrade to react version 17, run the command below.

yarn add react@17.0.1 react-dom@17.0.1
or
npm install react@17.0.1 react-dom@17.0.1

After this, you will want to update your codebase to remove all unused React imports within your JSX or TSX files. With React code mod, this process is automated.

Run the command below at the root of your project to remove all unused React imports.

npx react-codemod update-react-imports

2. Upgrade to Storybook v6

If you do not have Storybook v6 currently in your project, upgrade to the latest version by running the sb command as shown below. The command will upgrade all @storybook project packages to their latest versions.

npx sb upgrade

If you do not have any storybook installation yet. You can install Storybook and initialize it in the project using the Storybook CLI package. Run the scripts below to install and initialize React Storybook.

yarn add @storybook/cli --dev
npx sb init

3. Upgrade to Typescript 4.1+

If you are running typescript in your project, upgrade to typescript version 4.1 and change the entry value from to .

Without doing this, typescript will issue errors similar to below on any file that has JSX code, due to the missing React global import.

'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.

{
  "compilerOptions": {
    .......
    "jsx": "react-jsx", // <---- set as react-jsx instead of react
    "lib": ["dom", "es5", "es2015.collection", "es2015.iterable"],
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true
  },
  "include": ["src"],
  ......
}

Modify Storybook config to support React version 17

Storybook version 6 has zero configuration. It lets you do what is necessary for your project. With React version, we have to use the new JSX transform from the react team.

Install the latest versions of Babel loader, Babel preset react, and other necessary plugins using the command below.

yarn add --dev @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-nullish-coalescing-operator @babel/plugin-proposal-optional-chaining babel-loader

After this, we need to set up or modify the main.js file in the .storybook folder to support React version 17. Without this, Storybook will error out with the message:

Uncaught ReferenceError: React is not defined. This error is happening because all JSX files in the project no longer import the React global since we upgraded to React version 17.

The storybook main.js file is the file for defining custom Webpack configurations, Babel configurations, Addons integration and Stories' path.

In the configuration, we utilize babel-loader as shown below. copy the configuration and adjust to your needs. The main thing below is that we added the option to

Automatic runtime is a feature available in @babel/plugin-transform-react-jsxversion 7.9.0 which is used under the hood by preset-react. With this runtime enabled, the functions that JSX compiles to, which are functions from the will be imported automatically.

// .storybook/main.js
const path = require('path');

module.exports = {
  // defines where storybook will locate our stories
  stories: ['../src/**/*.stories.@(ts|tsx)'],

  addons: ['@storybook/addon-knobs/register'],

  babel: async (options) => {
    return {
      ...options,
    };
  },

  webpackFinal: async (config, { configType }) => {
    // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
    // You can change the configuration based on that.
    // 'PRODUCTION' is used when building the static version of storybook.

    // here we use babel-loader
    config.module.rules.push({
      test: /\.(ts|tsx)$/, <----- babel loader will process all of our stories
      loader: require.resolve('babel-loader'),
      options: {
        babelrc: false,
        presets: [
          '@babel/preset-typescript', <---- support for typescript
          [
            '@babel/preset-react', 
            {
              runtime: 'automatic', <---- support for react new jsx transform, needed for react version 17
            },
          ],
          ...................
        ],
        plugins: [
          ['@babel/plugin-proposal-nullish-coalescing-operator'],
          ['@babel/plugin-proposal-optional-chaining'],
          ['@teclone/babel-plugin-styled-components'],
          .........
        ],
      },
    });

    config.resolve.modules = [
      path.resolve(__dirname, '../', 'node_modules'),
      'node_modules',
    ];

    config.resolve.extensions.push('.ts', '.tsx');

    config.stats = 'verbose';

    // Return the altered config
    return config;
  },
};

Share With Friends