March 09, 2021

Beginning Building UI Components with Storybook

Storybook is a development environment and playground for developing and showcasing UI Components in isolation and interactively .

Storybook is inspired by Component-Driven Development (CDD) which is the a process that builds UIs from the “bottom up” starting with components and ending with screens.

With Storybook , we don’t have to worry about app requirements or business logic , we can create and visualize UI components in isolation .

You can develop entire UIs without needing to start up a complex dev stack, force certain data into your database, or navigate around your application.

When building our UI components , it’s very important to think about building reusable components by thinking and focusing on responsibility of the component rather than responsibility of the page .

Let’s start by boostraping our app using create-react-app

npx create-react-app ui-components

Once our app has been created , navigate to the projects directory and use storybook cli to install storybook by running below command :-

npx sb init

The above command will look in to your project dependecies and provide with best configurations available by installing required dev dependencies as well as creating stories folder with boilerplate stories to get you started.

The command will also add .storybook folder within our project . This folder has two configurations , main.js file which holds the storybook configurations as well as preview.js that has configurations for stories that we will create . You can go ahead and take a glimple on some of these configurations to have an understanding of predefined settings .

Now , let’s run our storybook

npm run storybook

Now , storybook should open on default port 6006 . You should see a screen like below :-

Storybook Homescreen

By default storybook will add boilerplate stories to get you started in a newly created stories folder inside src folder . Let’s go on and delete this folder so as we can write our own stories .

Now , when we refresh our page , we should have something like below:-

Storybook Homescreen

Now , we should be ready to write our stories . For this tutorial , we will build a simple component workspace which will have buttons .

A storybook is basically a collection of stories in which each story represent a single visual state of a component .

Let’s start writing story . Normally a story writing process will have three main files , a component file , a css file to style the component and a story file for the component . Its always a good idea to put the files in one folder .

Inside our src folder , let’s create a new components folder and inside the folder create a folder called Button with files including Button.js , Button.css as well as Button.stories.js

Let’s start with Button.js file , paste below code inside Button.js file :-

import React from "react";
import "./Button.css";

const Button = ({ variant = "primary", children, ...rest }) => {
  return (
    <button className={`button ${variant}`} {...rest}>
      {children}
    </button>
  );
};

export default Button;

From the above , ir should be straight forward that the Button component will have a variant prop which will add a css class to our button as well as adding any extra props . The Button component will also render children that will be wrapped inside it .

Let’s add Button.css file as below: -

.button {
  color: white;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  cursor: pointer;
  padding: 0.375rem 0.75rem;
  font-size: 1rem;
  line-height: 1.5;
  border: 1px solid transparent;
  border-radius: 0.25rem;
}

.primary {
  background-color: #007bff;
}
.secondary {
  background-color: #6c757d;
}

.success {
  background-color: #28a745;
}
.danger {
  background-color: #dc3545;
}

Now , let’s add our story file . We will write our story in a format called component story format . This format has a default export and one or more named exports .

The default export object has one unique mandatory property called title and should be unique within the entire project . This is also the text that will see in our left navigation panel in Storybook . Generally default export provide some metadata about the story and corresponding component .

Inside Button.stories.js , add the following:-

import React from 'react'
import Button from './Button'

export default {
  title:'Button',
  component:Button
}

From the above , we created our default export and add title as ‘Button’ , also a component property with a value of Button component we just created .

A component property is optional but its encouraged to always have it because some of the addons rely on this property .

Let’s modify our Button.stories.js file by adding a named export .

A named export is basically a react component and each one represents a story .

In our case , we will use the variant prop of our Button Component to have 4 stories that represent different button state . Below default export add your stories as below :-

import React from "react";
import Button from "./Button";

export default {
  title: "Button",
  component: Button,
};

export const Primary = () => <Button variant="primary">Primary</Button>;
export const Secondary = () => <Button variant="secondary">Secondary</Button>;
export const Success = () => <Button variant="success">Success</Button>;
export const Danger = () => <Button variant="danger">Danger</Button>;

Now , if we go back to our storybook, we should see something like below:-

Button Story

And Huraay ! We have our stories that represent different button states .

Another way to write stories is by using Args . Args let’s you define a master template for your stories which allows us to reduce amount of duplicate codes .

A story is a component with a set of arguments (props, slots, inputs, etc). “Args” are Storybook’s mechanism for defining those arguments as a first class entity that’s machine readable. This allows Storybook and its addons to live edit components.

The args mechanisms , start off by creating a template and exporting one or more stories using the template .

Let’s change our Success and Danger component to use args .

const Template = (args) => <Button {...args} />;

export const Success = Template.bind({});
Success.args = {
  variant: "success",
  children: "Success",
};

export const Danger = Template.bind({});
Danger.args = {
  variant: "danger",
  children: "Danger",
};

Another advantage of suing Args is being able to work with control add-on of strotybook . If you open storybook , and on right panel at the bottom , you will see Controls Tab, if you click it , you should have an ability to change the values of variant and children and change to reflect in realtime .

Lastly, we will talk about decorators in storybook .Picture a scenario where we want to apply same style to all of our stories . To do so we can go on and add a class to all stories or use something callled a decorator .

A decorator is simply a wrapper component for your stories .

When writing stories, decorators are typically used to wrap stories with extra markup or context mocking.

As stated above, decorator helps us to wrap our compoents with extra markup . Decorators can be added inside the individual story or in our default export as shown above .

export default {
  title: "Button",
  component: Button,
  decorators: [
    (Story) => (
      <div style={{ textAlign: "center" }}>
        <Story />
      </div>
    ),
  ],
};

We can also have global decorators which will add decorator to every single story in our storybook . To add a global decorator , navigate to .storybook folder and open preview.js file and add below code:

import { addDecorator } from "@storybook/react";
export const parameters = {
  actions: { argTypesRegex: "^on[A-Z].*" },
};

addDecorator((Story) => (
  <div style={{ textAlign: "center" }}>
    <Story />
  </div>
));

As your components workspace become bigger is very keen to group similar components. There are many implementations on this subject but one that stands out is Atomic Design .

Atomic Design allows building of design systems (inputs, buttons, other reusable components, etc.) in which components are grouped based on levels from the lowest level you will have atoms , molecules , organisms , Templates and pages . You can read more about atomic design in brand frost blog

That’s it for me now , i believe you can proceed from here and start building your UI Components workspace . See you again in the next post .


Robert Rutenge
Front-end Developer | ReactJS enthusiast | Problem Solver . Find me on twitter and Github