Merge projects seamlessly with our NEW GitLab integrationLearnmore
Product Design

Building a product component library and design system with Storybook

Niall Maher

This tutorial assumes you have some knowledge of modern JavaScript and Node.js installed on your system.

Storybook is a tool used by companies like Airbnb, Lyft and Salesforce to develop, test and document their component libraries and design systems.

It’s available for all the major view layer including libraries/frameworks (React, Vue, Angular, React Native, and Ember and more).

Storybook is a development sandbox that uses the concept of stories to document, test and build components.

A story usually contains a single state of one component, almost like a visual test case. Technically a story is a function that returns something that can be rendered to the screen.

Getting started

Let’s start a new project, I’ve created a folder called “clubhouse-storybook”, you can name yours whatever you like. Open your terminal in this directory and let’s get started!

The first thing we need to do is make sure we have a package.json in our root directory.

Run npm init and just hit enter through all the defaults options.

Storybook comes with a handy command line interface tool that makes getting a project started super fast. We need to install the storybook CLI tools globally and we do this by running:

npm i -g @storybook/cli

Once the CLI is installed we have to initiate the app, I’m going to be using React for this tutorial but feel free to choose a different flavour. To initiate a project with React we run npx -p @storybook/cli sb init --type react.

This takes a few seconds as it bootstraps our project into a development-ready state.

Note for adding storybook to an existing project:

If you are running storybook in an existing app with a library chosen you can use npx -p @storybook/cli sb init which scans your package.json dependencies and installs the dependencies to run for your chosen view layer.

Development

Once you have the previous steps completed it’s simple to run the development server, just run npm run storybook.

This by default will open your browser on http://localhost:6006/ and should look something like this depending on the version:

Now it’s time to crack open the code editor, finally!

Take some time to familiarise yourself with the folders and files. You should notice, each component has it’s own .stories.js extension, it’s in these files that we write the “story” for each component and get it on the screen.

I’m going to use a simple button as a sample for the sample of how to develop a component with 2 variations, a primary and a secondary.

Here’s a component a top designer sent me and I totally didn’t rip it off the Clubhouse homepage 😉

One more package I like to use is the prop-types package, I find it helpful for when we document components (you’ll see how soon). Install it with npm i -D prop-types.

First, we will create a new folder called “Button” inside the “stories” folder, this folder will hold all the files to do with the button we are about to create. The next steps are my inside the folder we will create a index.js (to export the component), Button.js, Button.stories.js and Button.css.

Then so we can focus on the story writing copy and paste the following into the corresponding files.

Button.js:

Button.css:

index.js:

Storytime!

Open your Button.stories.js file and import the following:

First, we import “React” since we are using JSX.

import React from 'react'

Then we import storybook which is a named export from @storybook/react.

import { storiesOf } from '@storybook/react'

Then action which is a named export from @storybook/addon-actions.

import { action } from '@storybook/addon-actions'

Then we import our Button component is a named export from index.js.

import Button from './'

To create a story we pass the group name of the component which is “Button”, this is what shows up in the sidebar of the storybook to organise the components, we then have to pass the “module” as a second parameter. Storybook needs a reference to the file/module where your story code is to enable hpt-module-replacement. If you do not supply it, you'd need to refresh your browser for every change you make to your component and story code.

Then we call the “add” function and pass the component name which is “Clubhouse Button” and the second parameter which is a function that returns our component. We also are going to pass the “action” addon to the “onClick” that we see used in the demo components.

 storiesOf('Button', module)
.add('Clubhouse Button', () => <Button onClick={action('clicked')}>Hello Button</Button>) 

You should now see a new component in the Button menu in your sidebar 👍

Now let’s add the variations into our story so we can see all of the different shades of the Button that I have pre-built.

(The divs are just there to create spacing between the components and make the story look a little prettier.)

The real power begins when we throw in a few add-ons. You can find the list of all of the available add-ons here. We already have the action addon out of the box but let’s add a new one to show you how easy it is.

One addon I always use in development and to show you how to get started with plugins is Info. Let’s throw it in and you will see why!

Install the Info plugin with npm i @storybook/addon-info.

First in our Button story, import withInfo which is a named a import:

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

Then we chain a decorator and pass it withInfo

storiesOf('Button', module)
.addDecorator(withInfo) // make sure it is the first decorator to avoid weirdness
.add('Clubhouse Button', () => ( … 

We can now pass a second argument to the add function which will take an object with an info property. The info takes a string as a value. We can use a Template String to create some structure to the documentation we pass to the info. The info is read as markdown in the storybook so feel free to make this easy to read by adding appropriate styling and emojis.

Here’s the complete code snippet for reference:

As a note, the info is a perfect place to add style guide notes so that developers know when to use these components. We will now see in our story preview a new button in the top right corner to “Show info”.

When you click this you will see we have a fairly comprehensive documentation set.

In the info section, you can copy the code snippets, see your documentation you passed with you add-info addon. The prop-types automatically generate a table showing if components are required and the type they expect. Another cool thing to note is in the propTypes table, you will see a “description” field. This is automatically generated when you structure your comments over your prop-types using a double star at the start of your comment such as:

Button.propTypes = {
/** Component takes a child node */
children: PropTypes.node.isRequired,

This, as you can see, takes minimal effort and allows us to take care of documentation as we develop (which is something we all dread writing). Adding usage notes from your designers, easily onboarding new hires and engineering productivity in general will be at an all-time high.

Find the completed code here.

If you have made it this far, thanks for reading!