Petros Kyriakoupersonal blog

I am a full stack web developer. Love tinkering all the time, especially anything javascript related.

Getting started with Storybook for ReactJS

December 10, 2019

Had you ever stuggled with finding a way to easily develop components in isolation without having to create a new environment each time, or trying to replicate a bug in one of the components you developed? Well, in recent years Storybook has arisen to help us do just that.

Storybook

Storybook is an open source tool at version 5.2 as I write this, and its purpose is to give the developer the ability to create UI components in isolation for all the popular frameworks like React, Vue, Angular and more so lets start our exploration.

For the purpose of this article I will be using ReactJS as the chosen framework to showcase storybook.

In case you just want to grab the code, you can do so here

Installation

Create directory and package.json

mkdir storybook-starter && cd storybook-start && npm init -y

Install storybook

npx -p @storybook/cli sb init --type react

Installation

After the installations are done, you should have a folder structure like the following

storybook-starter
 ├─> .storybook
 │   ├── addons.js
 │   └── config.js
 ├── package-lock.json
 ├── package.json
 ├─> stories
     ├── 0-Welcome.stories.js
     └── 1-Button.stories.js

Run storybook

npm run storybook

You should be able to observe the following

StorybookFirstStart

As you can see, by default storybook creates two sample stories. Stories are what you would refer to as component showrooms.

Each story conveys a story, and usually its for show casing a specific component and its various states/use cases.

Exploration

Exploring the directory structure

We have two major points of contact with storybook. The .storybook folder and the .stories.js files. In layman terms anything that has to do with configuration before/while starting Storybook goes into the .storybook folder.

Examples include, adding addons, extending default webpack config, adding presets etc.

The second point of contact with Storybook are the files ending in .stories.js.

If you look under .storybook/config.js you will find the following:

// .storybook/config.js
import { configure } from '@storybook/react';

// automatically import all files ending in *.stories.js
configure(require.context('../stories', true, /\.stories\.js$/), module);

The configure function is used to determine what folder(s) to watch for stories and in what extension they end. The default is looking in the stories folder one level above for files ending in .stories.js. You can easily extend this to include .story and .jsx like this

configure(require.context('../stories', true, /\.(stories|story)\.(js|jsx)$/), module);

Exploring installed addons

By default Storybook installs two addons

// .storybook/addons.js
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';

Actions addon

The actions addon which is used to display event handler data e.g on click when you an event it is displayed in the addons bar like so (more info in the exploring sample stories section)

ActionsAddonSample

Links Addon

The links addon can be used to link different stories together so that you can present to your manager your components linked together with a prototype kind of feel.

Exploring the sample stories

// 1-Button.stories.js
import React from 'react'
import { action } from '@storybook/addon-actions'
import { Button } from '@storybook/react/demo'

export default {
  title: 'Button'
}

export const text = () => (
  <Button onClick={action('clicked')}>Hello Button</Button>
)

export const emoji = () => (
  <Button onClick={action('clicked')}>
    <span role='img' aria-label='so cool'>
      😀 😎 👍 💯
    </span>
  </Button>
)

So, what do we have in the example above? CSF basically requires a default export with at least a title attribute which means the story title. For each substory you just export different constants and they automagically get picked up by Storybook as substories of the story Button in this case. Whats cool about it, even if you write weird naming syntax like some_custom_NAME it will convert to Some Custom NAME when displayed in Storybook.

So basically this should all be familiar if you have ever written React before. You just write normal React components like you are used to in isolation.

Action addon

In the example code above you might have noticed the import statement and its usage

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

...
<Button onClick={action('clicked')}>Hello Button</Button>
...

This is one of the default addons that usually come if you run the CLI command to install Storybook. What it does is display received data by event handlers (read more)

If you have followed any other tutorials in the past, you might notice that the syntax is different. Since v5.2 storybook team suggests writing your components in Component Story Format (CSF) instead of using the storiesOf API. It is much cleaner and thus easier to read and work with.

At the end this is what the story tree looks when rendered:

AutogeneratedStoryExample

Making webpack overrides

So, underneath Storybook uses webpack to find the stories and parse them. But if for whatever reason you would like to extend its functionality you might be left wondering where you would put that functionality. As an example we are going to setup the storysource addon which basically shows the source of the story as the name suggests in the addons bar.

Install Storysource addon

npm i -D @storybook/addon-storysource

Register the addon

Usually almost all addons needs to be registered first in this special file called addons.js

// .storybook/addons.js
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-storysource/register'; // new-line

Extend webpack config

This particular addon requires to add a new loader to the webpack config, and to do that we need to create a file named webpack.config.js under .storybook with the following:

// .storybook/webpack.config.js
module.exports = function({ config }) {
  config.module.rules.push({
    test: /\.stories\.jsx?$/,
    loaders: [require.resolve('@storybook/source-loader')],
    enforce: 'pre',
  });
 
  return config;
};

Remember to also add here the same rule for what files to check as in the config.js file. (e.g /\.stories\.jsx?$/)

Run storybook

npm run storybook

if everything went well, you should now have a new tab in the addons bar with the source code of the story.

StorySourceAddon

Next steps

Wheww we learned some good stuff (i hope) but these barely scratch the surface what Storybook can do. There are other pretty cool addons like knobs which autogenerates an interface so you can easily change the passed props to a component from the story itself and see the results on the fly. Or a documentation addon like surprise, surprise @storybook/addon-docs. You can even do testing, which you can find more info here.

Until next time people!

PS. What do you love most about Storybook so far?