Setting up a React + TypeScript + Storybook project

Josh Dando
Jan 5, 2019 · 6 min read

I recently started a project using React, TypeScript and Storybook and came across a few problems along the way, so I thought that writing this guide would be useful for other website developers out there.

The main problem

Ahh TypeScript, whilst it provides so many advantages to a project it is a real pain to setup with many JavaScript libraries such as storybook because of either the missing type definition files, the complicated setup process to get it working alongside other libraries/modules like webpack or outdated documentation. In the future I hope that many more libraries start offering support for TypeScript with updated documentation so our lives as developers would be made easier.

Why bother with TypeScript then?

TypeScript offers many advantages over JS such as type safety, tools including classes, interfaces etc. which feature in many other languages and of course in my opinion nicer syntax than regular javascipt.

For me personally the static types and the linting options in TypeScript are a neccessity for any big/professional project that you are going to undertake as it makes your code more consistent and robust to errors.

Setup instructions

We are going to use the microsoft react + typescript template project to get up and running. Why? Because it all works out of the box and includes other key libraries that you would commonly use on a project e.g. the testing library jest and enzyme. It’s also based off the create react app template which many of you may have used in the past to setup your react projects.

The template project can be found here: https://github.com/Microsoft/TypeScript-React-Starter

** Update **

Create react app now supports typescript 😃

npx create-react-app my-app --typescript

After entering this command skip to step 3


If you already have a create-react app project setup with JS then we can easily convert it into TypeScript with the following npm modules.

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

If you have existing JS components that you wish to convert to TypeScript then this library maybe helpful to you.


Otherwise lets begin from scratch!

  1. Install create react app using npm
npm install -g create-react-app

2. Next we tell the project that we want to use TypeScript. The project will then install and use these scripts for when we want to run/build/eject the project.

create-react-app my-app --scripts-version=react-scripts-ts

Now we have a working project we can test it works by running:

npm run start

3. Next we want to install Storybook as a dev dependency for react using npm. We install it as a dev dependency because storybook wont be required for a production build.

npm i -D @storybook/react

4. Before we can get storybook to work we need to add a couple more things. Firstly we need to create a .storybook folder in the root of our project. Inside this folder we will specify four different files. Create these empty files inside your .storybook folder.

.storybook
- addons.js
- config.js
- tsconfig.json
- webpack.config.js

Note: we won’t need the addons.js file for now but in the future this is where you register storybook add on libraries. Some of the ones I find the most useful are: a11y, actions, knobs, storyshots and viewports. A full list of addons can be found here.

5. Inside your .storybook/config.js file we need to tell storybook where to find our stories. Now on the storybook documentation website they have their stories inside a stories folder; however I prefer to keep all the stories within the folder of the component to keep it all together. If you wish to follow the original documentation then change the string ‘../src/components’ to ‘../stories’

import { configure } from '@storybook/react';const req = require.context('../src/components', true, /.stories.tsx$/);function loadStories() {
req.keys().forEach(req);
}
configure(loadStories, module);

6. Moving on to our next file we created the .storybook/tsconfig.json we can use the following json to get our storybook to compile typescript into js. The most important attribute of this json file is the line: "jsx": "react" which will allow us to use JSX in TypeScript as long as we continue to use .tsx files.

{
"compilerOptions": {
"baseUrl": "./",
"allowSyntheticDefaultImports": true,
"module": "es2015",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": false,
"jsx": "react",
"moduleResolution": "node",
"rootDir": "../",
"outDir": "dist",
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"declaration": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"build",
"dist",
"scripts",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts",
"**/*/*.test.ts",
"examples"
]
}

7. To compile our storybook we need to take advantage of a couple of libraries. First install the following:

npm i -D awesome-typescript-loader
npm i -D react-docgen-typescript-loader
npm i -D react-docgen-typescript-webpack-plugin

8. Now copy the following code into .storybook/webpack.config.js

const path = require("path");
const SRC_PATH = path.join(__dirname, '../src');
const STORIES_PATH = path.join(__dirname, '../stories');
//dont need stories path if you have your stories inside your //components folder
module.exports = ({config}) => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
include: [SRC_PATH, STORIES_PATH],
use: [
{
loader: require.resolve(’awesome-typescript-loader’),
options: {
configFileName: './.storybook/tsconfig.json'
}
},
{ loader: require.resolve(’react-docgen-typescript-loader’) }
]
});
config.resolve.extensions.push(’.ts’, '.tsx’);
return config;
};

9. Alright, phew nearly done. We need one last bit of setup then we can create a simple component and story to test everything is working correctly. So far we have told storybook where to find our stories and how to compile them into JS. Now we need to tell npm to run storybook.

Inside your package.json file underneath the other scripts include the following:

“storybook”: “start-storybook -p 9001 -c .storybook”

10. Ok, so all the setup is now complete and we can now work on our first component and create a story for it. Create a src/components folder and inside that create a button folder. This folder will contain both the component and the story.

In the Button.tsx file:

import * as React from 'react';export interface IButtonProps {
children?: React.ReactNode,
onClick?: (e:any) => void
}
const styles = {
border: '1px solid #eee',
borderRadius: 3,
backgroundColor: '#FFFFFF',
cursor: 'pointer',
fontSize: 15,
padding: '3px 10px',
margin: 10,
};
const Button: React.SFC<IButtonProps> = (props) => (
<button onClick={props.onClick} style={styles} type="button">
{props.children}
</button>
);
Button.defaultProps = {
children: null,
onClick: () => {}
};
export default Button;

Note that we are using a stateless functional component (fancy I know). More information about them can be found in my future blog post.

If you get any errors appear in your IDE this is because of the TSlint rules. To ammend this we need to go inside our tslint.json file and add the following rules:

"rules": {
"object-literal-sort-keys": false,
"no-empty": [true, "allow-empty-functions"]
}

11. Lastly we can create our story file for our button component. Inside the button component or if you decided to follow the storybook documentation you will need to create the file inside your stories folder. We name this file button.stories.tsx

import { storiesOf } from '@storybook/react';
import * as React from 'react';
import Button from "./Button";
storiesOf("Button", module)
.add("with text", () => (
<Button>Hello Button</Button>
))
.add("with some emoji", () => (
<Button>😀 😎 👍 💯</Button>
));

12. We have now reached the moment of truth. Will this project run? Well to find out we can run the script through npm like so:

npm run storybook

Fixing common errors

WARN   Failed to load preset: "/Users/xyz/my-app/node_modules/@storybook/react/dist/server/framework-preset-cra.js"
ERR! TypeError: Cannot destructure property `createHash` of 'undefined' or 'null'.

Installing mini-css-extract-plugin fixes this problem for us. Just do:

npm i -D mini-css-extract-plugin

Another common error is:

ERR! Error: => Create a storybook config file in ".storybook/config.{ext}".

This is because you have incorrectly named the storybook folder, it should be .storybook and exist on the root of your project.

We should now have our lovely storybook!

Conclusion

Congratulations for everyone who stayed with me on this one. I know its been a long post but hopefully its all been worth the journey and you learnt something along the way. If this guide helped at all I would appreciate a like on my very first blog!!

Check out part 2 where we improve our storybook experience by using storybook addons

Josh Dando

Written by

Full-stack software engineer at mercarto.com. CTO and Co-founder at swooft.io

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade