Storybook for React.js
Tags: react, storybook, ui, frontend
Published: 2022-05-03 20:13:51

Often I face the problem that I want to start UI development from bottom to top: start with designing buttons, inputs and other small components and only after that create pages and set routing.

This problem is solved with… **Storybook**!

Storybook is a tool that allows developing components in isolation.

According to [official web site of Storybook:](https://storybook.js.org/)

> Storybook is an open source tool for building UI components and pages in isolation. It streamlines UI development, testing, and documentation.

Let’s look at basic Storybooks functionality with a simple example of Button component.

In this example we will add Storybook to React App, but here is the list of libraries/frameworks with which you can implement Storybook: React, Vue, Angular, Web Components, Ember, HTML, Svelte, Preact.

First of all let’s install Storybook.

```bash
npx create-react-app my-app
cd my-app
npx sb init
```

Typescript is also supported and it doesn’t require extra configuration. Don’t forget to install types.

```bash
npm i @storybook/react
```

Storybook uses Webpack5 and if you have a previous version so run: 

```bash
npx sb@next automigrate
```

To run the application use:

```bash
npm run storybook
```

Browser should open localhost on port 6006 and you will see this. If you want to change the port you can do it in package.json file.


![Storybook interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p57ouw2bwy1vtrpmvdpb.png)

In larger projects you can also add folders in sections for convenience. You can do it later inside of your stories.

Current part of the code in Button.stories.js

```js
export default {
 title: 'Example/Button',
...
};
```
Schema how you can split it into sections and folders:

```js
export default {
 title: 'NameOfSection/NameOfFolder/NameOfComponent',
...
};
```

After every package is downloaded we have updates: we see folder .storybook inside the main folder and folder screens inside of src folder with Button.js, Button.stories.js and other files.


![Initial scr folder structure of Storybook](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yj00dun48vlef69z91ag.png)

I prefer dividing code logically into separate files so I deleted everything in the src folder and created separate folders for components and stories.


![Updated src folder structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aemr5l6ggzw3n390fc21.png)

Let’s have a look what we have in Button.js

```js
import PropTypes from "prop-types";
 
function Button({
 label,
 backgroundColor = "red",
 color = "black",
 size = "md",
 handleClick,
}) {
 let scale = 1;
 if (size === "sm") scale = 0.5;
 if (size === "lg") scale = 2;

 const style = {
   backgroundColor,
   padding: `${scale * 0.5}rem ${scale * 1}rem`,
   border: "2px solid black",
   borderRadius: "25px",
   color,
 };

 return (
   <button onClick={handleClick} style={style}>
     {label}
   </button>
 );
}
 
Button.propTypes = {
 label: PropTypes.string,
 backgroundColor: PropTypes.string,
 size: PropTypes.oneOf(["sm", "md", "lg"]),
 color: PropTypes.oneOf(["blue", "white", "black"]),
 handleClick: PropTypes.func,
};
 
export default Button;
```
Function Button takes parameters by default and inside of object style we add CSS and return the component.
After that we define the type of props of the component by adding or type of props with or without options for choice.

Let’s look through Button.stories.js

```js
import Button from "../components/Button";
 
export default {
 title: "Components/Controls/Button",
 component: Button,
 argTypes: {
   handleClick: { action: "logging it in action section" },
 }
};
 
const Template = (args) => <Button {...args} />;
 
export const Green = Template.bind({});

Green.args = {
 backgroundColor: "green",
 label: "Middle Green button",
 size: "md",
 color: "black",
};
 
 
export const Pink = Template.bind({});

Pink.args = {
 backgroundColor: "pink",
 label: "Middle Pink Button",
 color: "blue",
 size: "md",
};
 
 
export const Small = Template.bind({});

Small.args = {
 backgroundColor: "blue",
 label: "Small button",
 size: "sm",
 color: "white",
};
 
export const Large = Template.bind({});

Large.args = {
 label: "Very very very large button",
 size: "lg",
 color: "black",
 backgroundColor: "red",
};
```
To activate actions we add argTypes and define function handleClick that logs out events.

Let's check what happened to our stories on local host.


![Green button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udbuqyrgvcdwn8zf339m.png)


![Large button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vz7nyzsd4oc1chg130v.png)


![Pink button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmwcba4hl3y075702oo5.png)


![Small button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9xb95c0xw5kgwtvj1bs.png)

By hand you can change background color, label, size and color as well as click on the button and get action.

![Green button change](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kl7expsuic8ytruxg891.png)

![Button action](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mathtw3z4xkxlptazhg4.png)

I clicked the undo icon and got this component in the initial state.

Also we can add styling to the stories with the help of a decorator. 
For example let’s add margin to story with decorators according to schema

Component.stories.js
```js
export default {
 …..
  decorators: [
    (Story) => (
      <div style={{ margin: '5em' }}>
        <Story />
      </div>
    ),
  ],
};
```

Button.stories.js

```js
import Button from "../components/Button";
 
export default {
 title: "Components/Controls/Button",
 component: Button,
 argTypes: {
   handleClick: { action: "logging it in action section" },
 },
   decorators: [
     (Story) => (
       <div style={{ margin: "5rem" }}>
         <Story />
       </div>
     ),
   ],
};
 
const Template = (args) => <Button {...args} />;
 
export const Green = Template.bind({});

Green.args = {
 backgroundColor: "green",
 label: "Middle Green button",
 size: "md",
 color: "black",
};
 
 
export const Pink = Template.bind({});

Pink.args = {
 backgroundColor: "pink",
 label: "Middle Pink Button",
 color: "blue",
 size: "md",
};
 
 
export const Small = Template.bind({});

Small.args = {
 backgroundColor: "blue",
 label: "Small button",
 size: "sm",
 color: "white",
};
 
export const Large = Template.bind({});

Large.args = {
 label: "Very very very large button",
 size: "lg",
 color: "black",
 backgroundColor: "red",
};
```

Let's check in the browser if we had any changes of margin.

![Button with margin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kg2yqn14pf35irhvrxdo.png)

Yes, it worked :)

It is only a beginning and you can continue with learning advanced Storybook's topic such as Storybook Addons and testing.

Continue enjoying developing UI with [Storybook official docs](https://storybook.js.org/docs/react/get-started/introduction).