Storybook: Doing Component Development the right way

Storybook: Doing Component Development the right way

Nowadays, if you have ever tried to build a user interface you might have come across many problems. Building these interface components is a very expensive and effort consuming task. We have designers, product managers and many developers working on a single project.

The modern User interfaces are built with thousands of UI components that are reused between different screens and different user interfaces and sometimes between different products inside the same company to make the design look consistent. Now usually in these scenarios, there are design systems in place with the catalogs of all the reusable components in one place. This helps improves the productivity of the developers by up to 30–40%. Modern UI interfaces are build from thousands of reusable UI components

Typical use case of a design system

Now here Design System contains reusable components which can be used among different application to build complex, durable and accessible user interfaces. Since both designs and developers contribute to a Design system so it servers as a “single source of truth” where designs can test the Component design they build in different scenarios.

When Do we need a design system?

Now despite all the hype and pros of a design system, it is not for everyone. If you are working on a single project, you will be better off without a design system. It will just add to the complexity of your application. But if you sharing your components across different projects then building a designing a design system makes sense for you rather then copy and pasting component from one place to another.

Design System workflow

okay, So what exactly does storybook do?

Storybook is used by developers for the following reasons

  1. Building the UI component in isolation

  2. To prevent UI bugs

  3. Standardizing Styling across different Projects

  4. Distributing UI Components among different projects

Okay, Great but how will Storybook help me?

UI/UX Designers

UI/UX designs can go into the storybook to see how exactly the components look and feel in different scenarios, see all the states of the component, how they are behaving in these different state changes and provide your valuable feedback.

Developers

Developers can easily share these components between different projects and see how exactly they are being used what properties do these components have and how they can extend them. This speeds up the development process since now you just have to build your component once and just import and use it elsewhere. Codesharing becomes easier and side effects can be easily handled at one single place.

Getting Started with React Storybook

Now the storybook book can be used with almost every frontend framework possible, and there are a lot of them. Why can we stick with just one and make it standard? Okay, enough of the framework rant. let us start with React. You can find documentation for other frameworks here.

Installing React Storybook

Now since Storybook is a part of the javascript ecosystem you can install it using your favorite package manager. For this introduction, I’m going to use yarn but npm also works the same way.

yarn global add @storybook/cli

It will globally install storybook CLI on your system. if you don’t want to permanently install storybook CLI you can also use npx. Read more about this here.

Now all we need a React application. And we are going to create that using create-react-app. Install creat-react-app in your by running following command in your system.

yarn create react-app storybook-intro --typescript
cd storybook-intro

Folder structure generated

You can read more about create-react-app here. Now let’s start by adding storybook to our project.

Now run the following command to add storybook to your project.

getstorybook

Now if you notice there are some extra folders add to your project.

Folder structure after running getstorybook

.storybook is used for configuring storybook. we will do that later.

Now run

yarn storybook

Now if you go to http://localhost:9009/ you will see the following page. Now make sure to restart your server whenever you change any config or add any new package.

[http://localhost:9009/](http://localhost:9009/)

Now let’s start building some component then we will see how we can configure storybook and make it more powerful and fully utilize its purpose.

So let’s create a basic component. Let’s start with a button.

import React from "react";

import "./index.css";

interface IButtonProps {}
const Button: React.FC<IButtonProps> = () => {
return (
<>
<button className="button">Primary Button</button>
</>
);

};

export default Button;
export { Button };

Add the following CSS

.button {

background-color: #4caf50; /* Green */

border: none;

color: white;

padding: 15px 32px;

text-align: center;

text-decoration: none;

display: inline-block;

font-size: 16px;

margin: 4px 2px;

cursor: pointer;

}
.primary {
background-color: #008cba;
}

.danger {

background-color: #f44336;

}

Now let’s add a story for this component. btw you can delete the stories folder we don’t need anyway. Now create a new file button.stories.TSX alongside your button component.

New Folder structure

Now let’s create our first story inside button.stories.tsx

import React from "react";

import { Button } from "./index";

export default {

title: "Button",

component: Button

};

export const Primary = () => <Button>Hello Button</Button>;

Let’s start the storybook server again and let’s see how our story looks.

Storybook after removing stories folder

Okay, we don’t see our newly added story. But why is that? If we go to .storybook/config.js file we see that the storybook is configured for javaScript, not TypeScript.

config.js

Now let’s configure it for TypeScript. This can easily be solved by just adding the correct regex in stories.

../src/**/*.stories.(ts|tsx|js|jsx)

new config.js

It will pick all the JSX/TSX/js/ts files in the project. Now if you go to localhost:9009 we see our story. Make sure you restart the storybook server since this is a configuration change.

Finally, we see our story

Now let’s make our component a little more standard so we can expect some props and make changes.

import React from "react";

import "./index.css";

interface IButtonProps {

buttonText: string;

primary?: boolean;

danger?: boolean;

}

const Button: React.FC<IButtonProps> = props => {
const { buttonText, primary, danger } = props;
let styles;
primary ? (styles = "primary") : danger ? (styles = "danger") : (styles = "");

return (
<>
<button className={"button" + " " + styles}>{buttonText}</button>
</>

);

};

export default Button;
export { Button };

Now since we update we also need to update our storybook component to send in these props which we just added to our component.

import React from "react";

import { Button } from "./index";

export default {

title: "Button",

component: Button

};

export const Primary = () => (

<Button buttonText="Primary Button" primary={true} />

);

export const DangerButton = () => (

<Button buttonText="Danger Button" danger={true} />

);

Now if we go back to our storybook we see 2 stories. One with Primary Button and one with Danger Button.

Now, This is just the isolation part of the development. we developed a storybook component in isolation but how do we tell other developers that we are expecting all these props like button text, primary, danger and they can change these to change the appearance of the button. That’s where storybook add-ons come to play which makes storybook so powerful for building a common component library.

Storybook has several recommended add-ons

  • a11y — Test components for user accessibility in Storybook

  • actions — Log actions as users interact with components in the Storybook UI

  • knobs — Interactively edit components input data in the Storybook UI

  • source — View a Stories code within the Storybook UI

  • viewport — Change display sizes and layouts for responsive components using Storybook. This can help you build responsive layouts

You can see all the add-ons here. On top of this, you can create your addon if you want. Learn more about that here.

Now let’s add some add-ons to our little project. Let’s start with the knobs add-on so that we can interact with our component.

Installation

First, we need to install the add-on to our project

yarn add @storybook/addon-knobs @types/storybook__addon-knobs

Now register knob in your .storybook/main.js file

// .storybook/main.js

module.exports = {
stories: ["../src/**/*.stories.(ts|tsx|js|jsx)"],

addons: [

"@storybook/preset-create-react-app",

"@storybook/addon-actions",
"@storybook/addon-links",
"@storybook/addon-knobs"
]
};

Now let’s add the newly added knob there. Go to the .storybook folder and create new file config.js and add the newly added addon there. Adding addon like this will it to all the stories. If you just want want to add the addon to only one story that can also be done. You can read about that here. But if you are building a library you won’t be adding add-ons one by one to each file. So let’s start by creating a config.js file.

// .storybook/config.js

import { withKnobs } from "@storybook/addon-knobs";
import { addDecorator } from "@storybook/react";

addDecorator(withKnobs);

Folder structure after adding config

Now before we see any changes with our storybook we need to use knob inside our story. So now go to button.stories.tsx and use the knob. Now the knob provides us with a lot of knobs like text, boolean, object depending on your data type. Now in our case, we only need text and boolean since these are the only types we support in our button component. Now import the appropriate knob from the @storybook/addon-knobs

// Button/button.stories.tsx

import { text, boolean } from "@storybook/addon-knobs";

Now go to the component story and use the knob as follows.

// PrimaryButton inside Button/button.stories.tsx
<Button

buttonText={text("Button Text", "Primary Button")}

primary={boolean("Primary button", true)}

/>

// DangerButton inside Button/button.stories.tsx

<Button

buttonText={text("Button Text", "Danger Button")}

danger={boolean("Danger Button", true)}

/>

Now if you go back to localhost:9009/ you see your newly added knobs in action.

Knobs in action

Now we can change the text of the button and use boolean to set the state of the button and see how the button behaves when its state changes.

Another very useful addon is the info. Now when you install it every story in your library gets a Documentation page. These can be used for documenting your component so the other developer can understand how and why it can be used.

To add this addon just install the addon

yarn add @storybook/addon-info @types/storybook__addon-info

Now, lets first register it to our main.js file onto our addons.

// .storybook/main.js
module.exports = {

stories: ["../src/**/*.stories.(ts|tsx|js|jsx)"],

addons: [

"@storybook/preset-create-react-app",

"@storybook/addon-actions",

"@storybook/addon-links",

"@storybook/addon-knobs",

"@storybook/addon-info"

]

};

After this, we also need to configure this addon to work with our storybook so go to config.js and register this new addon.

// .storybook/config.js

import { withKnobs } from "@storybook/addon-knobs";

import { addDecorator, addParameters } from "@storybook/react";

import { withInfo } from "@storybook/addon-info";



addDecorator(withInfo);

addDecorator(withKnobs);

Now go back to your button story and configure this addon.

import PropTypes from "prop-types";



import { Button } from "./index";

export default {

title: "Button",

component: Button,

parameters: {

info: { inline: false }

}

};

Button.propTypes = {

buttonText: PropTypes.string.isRequired,

primary: PropTypes.bool,

danger: PropTypes.bool

};

We just need can use the info the parameter to either pass certain options or specific documentation text to your stories. and add prop types to show the props received by the button component info. It is also possible to disable the add-on entirely. Depending on the scope at which you want to disable the addon. Just pass info:{disable:true} Now if we go back to localhost:9009/ after restarting the server we see a new section called show info on the top right corner.

Now if we go to docs we don’t see anything interesting. Just the component and how it is being used.

Now let’s add some documentation to better elaborate this component what it does and how it should be used. We can pass another key to info the called text which describes what the component does and how it can be used.

Description for our button component

Now after adding the text if we go back to localhost:9009/ and click on show info we see our documentation of the component.

Show info knob in action

Conclusion

As you see throughout this article, Storybook is easy to use and has a lot of add-ons and make it easy for the code to be shared among different project along with the proper documentation and we can build all our components in isolation and all the team members can see what components have been built and how they can use these components. And if a new person joins he won’t have to worry whether a component has been build or how to use a component. This significantly reduces the development time and helps strengthen your interfaces and make them consistent throughout different projects.

P.S :- all code for this article is available here