DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Storybook for React.js
Julie Cherner
Julie Cherner

Posted on

Storybook for React.js

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:

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.

npx create-react-app my-app
cd my-app
npx sb init
Enter fullscreen mode Exit fullscreen mode

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

npm i @storybook/react
Enter fullscreen mode Exit fullscreen mode

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

npx sb@next automigrate
Enter fullscreen mode Exit fullscreen mode

To run the application use:

npm run storybook
Enter fullscreen mode Exit fullscreen mode

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

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

export default {
 title: 'Example/Button',
...
};
Enter fullscreen mode Exit fullscreen mode

Schema how you can split it into sections and folders:

export default {
 title: 'NameOfSection/NameOfFolder/NameOfComponent',
...
};
Enter fullscreen mode Exit fullscreen mode

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

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

Let’s have a look what we have in Button.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;
Enter fullscreen mode Exit fullscreen mode

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

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",
};
Enter fullscreen mode Exit fullscreen mode

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

Large button

Pink button

Small button

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

Green button change

Button action

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

export default {
 …..
  decorators: [
    (Story) => (
      <div style={{ margin: '5em' }}>
        <Story />
      </div>
    ),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Button.stories.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",
};
Enter fullscreen mode Exit fullscreen mode

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

Button with margin

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.

Top comments (2)

Collapse
diballesteros profile image
Diego (Relatable Code)

I've really been enjoying using storybook. It just feels so clean. Members of my team really appreciate the docs you can integrate too.

Collapse
andrewbaisden profile image
Andrew Baisden

Storybook is awesome! Used it to develop my most recent portfolio and have an upcoming article about it.

🌚 Browsing with dark mode makes you a better developer.

It's a scientific fact.