A living style guide for React.js -starring StoryBook

christoffer noring
ITNEXT
Published in
7 min readJan 31, 2019

--

Imagine when your project grows and the number of components and developers with it. It might get hard to keep track of all the components and how they are rendered differently depending on what input they get. For those situations, there exists a number of tools where you can visually display your components — a style guide. This article is about one such tool called Storybook.

In this article we will cover how to:

  • set up Storybook, Storybook is a NPM library so it’s quite easy to install and a simple command line call with augment your project with StoryBook
  • run and explore Storybook, here we will start up StoryBook and explore it’s UI so you get a feeling for what it’s like to display and manage your components
  • create and manage Stories, StoryBook consists of stories where each story is a component that it displays in the UI
  • improve our set up, there are ways of organizing your Stories so that it scales so that when the number of components grows StoryBook grows with you

It should be said that Storybook is agnostic in terms of framework, it supports Angular, Vue React and many more, full list of supported frameworks , even though this article shows how to use it with React

Install & Set up

How would we install it? We need to install a CLI for this by typing:

cd my-react-vue-angular-app
npx -p @storybook/cli sb init

Adding Storybook to your project

Once we have finished installing then we can go ahead and create our React project. From the root of the project we then type the following:

getstorybook

This will install all the needed dependencies and it will also do the following changes to your project:

  • adds a storybook-demo/.storybook/ directory
  • adds src/stories
  • adds the following to package.json and the scripts tag: "storybook": "start-storybook -p 9009 -s public"

The .storybook directory contains the following:

  • addons.js, you can augment Storybook further but that is outside the scope of this article
  • config.js, config.js tells storybook where our stories can be found. The stories directory contains an index.js and the stories we add, which are something that storybook picks up and renders as a HTML page.

The /stories/index.js looks like this:

import React from 'react'; 
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { Button, Welcome } from '@storybook/react/demo';
storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />); storiesOf('Button', module)
.add('with text', () =>
<Button onClick={action('clicked')}>Hello Button</Button>
)
.add('with some emoji', () => (
<Button onClick={action('clicked')}>
<span role="img" aria-label="so cool"> 😀 😎 👍 💯 </span>
</Button>
)
);

Let’s look at the code that produces this in stories/index.js. Every call tostoriesOf('name of module', module) produces a section, a section is a new entry the UI menu and contains a specific component.

We can then chain a call to the section by repeated calls to add('name of component variant', () => ()). Every chained call produces sub-variant of a component. In the above code, we do two different calls to add() and thereby produce two different variants, which we will soon see when we bring up the UI.

Now the add() method is worth looking closer at:

.add('with text', () => 
<Button onClick={action('clicked')}>Hello Button</Button>)

The second argument renders out a React component and we seem to be able to set whatever property we want in there.

Run

Let’s bring up the UI next. We do so by calling the following in the terminal:

yarn storybook

It will start up story book project and we can thereafter navigate to http://localhost:9000 to see our stories. It will look like this:

As you can see above we have our section, created by our call to storiesOf() and we have our two variants with text and with some emoji produced by our calls to add().

Let’s try to create our own stories next.

Add a story

Let’s do the following:

  • create, a Todos.js component
  • add an entry, of that component to stories/index.js

First off, lets’ look at the code for Todos.js:

// Todos.js import React from 'react'; 
import styled from 'styled-components';
const Todo = styled.div`
box-shadow: 0 0 5px grey;
padding: 10px;
margin-bottom: 10px;
`;
const Todos = ({ todos }) => (
<React.Fragment>
<h3>List of todos</h3>
{todos.map(t => <Todo key={t.title}>{t.title}</Todo>)}
</React.Fragment> );
export default Todos;

Now let’s add the entry to stories/index.js:

import React from 'react'; 
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';
import { Button, Welcome } from '@storybook/react/demo';
import Todos from '../Todos';
import mocks from './mocks';
storiesOf('Welcome', module)
.add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
storiesOf('Button', module)
.add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
.add('with some emoji', () => ( <Button onClick={action('clicked')}> <span role="img" aria-label="so cool"> 😀 😎 👍 💯 </span> </Button> ));
storiesOf('Todos', module)
.add('with todos', () => <Todos todos={mocks.todos} />)

Let’s highlight on our additions. First, we import what we need:

import Todos from '../Todos'; 
import mocks from './mocks';

The mocks.js is a file we created to supply our components with data. We chose to place it in a separate file so we don't clutter up stories/index.js. The mocks.js is a very simple file looking like this:

// stories/mocks.jsconst mocks = { 
todos: [
{ title: 'test' },
{ title: 'test2' }
]
};
export default mocks;

Now back to our stories/index.js file and let's look at the part where we set up our story:

storiesOf('Todos', module) 
.add('with todos', () => <Todos todos={mocks.todos} />)

As you can see we set up the story and define a variant of that story by calling add. We can definitely define more variants if we need, remember, the point is to showcase all the different variants that exist if component so you understand what parameters trigger your component to behave differently.

Let's have a look at the result:

Above we see that we have gotten the entry Todos, produced by our call to storiesOf() and our variant with Todos is also shown, produced by our call to add().

As you can see it is quite easy to demo any component, and its variants, that you have in your project. Adding a new variant to Todos or a new section is quite easy and hopefully, I’ve made it clear above how you do that.

Improving — Dedicated story folders

Ok great now we know how to create our components and display them in a storybook. However your storybook/index.js might become quite cluttered so you may want to opt for a solution that scales better. Let's have a look .storybook/config.js:

import { configure } from '@storybook/react'; function loadStories() { 
require('../src/stories');
}

configure(loadStories, module);

The loadStories() function allows us to add more entries in there, which is perfect. Let's do that next:

// .storybook/config.jsimport { configure } from '@storybook/react'; function loadStories() { 
require('../src/stories');
require('../src/stories/common');
}
configure(loadStories, module);

As you can see we can now point out ../src/stories/common as yet another place our stories can exist in. That directory now needs to consist of an index.js and we can write our stories per usual like so:

import React from 'react'; 
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import Button from '../../common/Button';
storiesOf('Button', module)
.add('with text', () => <Button title={'title for a button'}>text</Button>)
.add('with markup', () =>
<Button title={'title for a button'}>
<h3>My button</h3>
<span>button text</span>
</Button>
)

The component we render above has the following code:

import React from 'react'; 
import styled from 'styled-components';
const InnerButton = styled.button`
padding: 20px;
box-shadow: 0 0 5px grey;
border-radius: 3px;
:focus { border: solid 1px black; }
`;
class Button extends React.Component {
render() {
return (
<InnerButton { ...this.props } >
{this.props.children}
</InnerButton> );
}
}
export default Button;

Our style guide now renders like so:

and for the second button variant:

So if you start to have components divided up into different directories it can be a good idea to bring that directory structure with you.

Summary

We set out to discover briefly why we might need a style guide. The short answer was that as our project grows so does the number of components and it might be hard to keep track of which one to use in what situation. A style guide is a good option to keep track of the above even though it does require a level of commitment from the developers to update it.

Hopefully, you know have a good understanding of how to set up a React project with Storybook and start adding your components, as stories.

Further reading

Here is the link to the Official docs on StoryBook

This article covers different types of Style Guide libraries, not just this one, well worth a read Great article on different style guide for React

My Twitter

--

--