We want you to make reusable UI components

A year ago I was a young developer starting his journey on React. My client came to see my team and told us: “We need to make reusable components”, I asked him: “What is a reusable component?” and the answer was “This project is a pilot on the subject”.

2 months later another developer tried to use our components and the disillusion started: despite our efforts, our component were not reusable 😱

At the time we managed to work with him to improve our code so that he could use it, but how could we have avoided the problem?

The answer was given to me by Florian Rival, a former developer at Bam, now working for Facebook: Storybook !

Storybook, what is that?

It is an open source visual documentation software (here is the repo). It allows you to display the different states of your component. The cluster of all the different cases for your component are called the component stories.

This allows you to visually describe your components : anyone who wants to use your components can just look at your stories and see how to use it. No need to dig in the code to find all the use cases, they are all there!

A picture is worth a thousand words, so just check the best example I know, the open Storybook of Airbnb.

One interesting thing to note is that it’s working with Vue, Angular and React!

Usage example

Let’s make an example to explain this better to you. I will use a react todo list, I started with the one on this repo.

Then I added Storybook to the project, I won’t detail this part as the Storybook doc is very good. I would say it takes approximately 20 minutes to add storybook to your project, but might take longer to properly setup your asset builder.

Now I’ll focus on the component FilteredList that display the todos, first it looked like this:

import React from 'react';
import styled from 'styled-components';
import TodoItem from './TodoItem';

const StyledUl = styled.ul`
  list-style: none;
`;

const StyledP = styled.p`
  margin: 10px 0;
  padding: 10px;
  border-radius: 0;
  background: #f2f2f2;
  border: 1px solid rgba(229, 229, 229, 0.5);
  color: #888;
`;

export default function FilteredList(props) {
    const {items, changeStatus} = props;

    if (items.length === 0) {
        return (
            <StyledP>There are no items.</StyledP>
        );
    }

    return (
        <StyledUl>
            {items.map(item => (
                <TodoItem key={item.id} data={item} changeStatus={changeStatus}/>
            ))}
        </StyledUl>
    );
}

(It is not exactly the same as the one on the repo, I used styled-component instead of plain css)

TodoItem is the component that displays an element of the list.

Here we can see there are two different branches in the render: the nominal case and the empty state.

Let’s write some stories, I created a file FilteredList.stories.js and added this inside:

import React from 'react';
import { storiesOf } from '@storybook/react';
import FilteredList from "./FilteredList";

const data = [{
    id: 1,
    completed: true,
    text: 'Jean-Claude Van Damme'
}];

storiesOf('FilteredList')
    .add('Nominal usage', () => (
        <FilteredList items={data} changeMode={() => void 0}/>
    ))
    .add('Empty state', () => (
        <FilteredList items={[]} changeMode={() => void 0}/>
    ));

So what did I do here?

I defined placeholder data in a variable for the component props.

We use the function storiesOf from storybook, this function takes the name we want to give to the group of stories as entry param.

Then we add some stories with .add. It works pretty much like jest or mocha’s describe or it in tests, it takes the name of the story and a function that returns the component to render.

Here’s what it looks like:

Screenshot from 2018-09-13 18-26-28
Screenshot from 2018-09-13 18-26-53

It’s rather simple but it’s working, we see the two different cases.

What if we add other branches? Let’s say the parent component of FilteredList is calling an API to get the list and so we have to add a loading and error state.

export default function FilteredList(props) {
    const {items, changeStatus, isLoading, error} = props;

    if (isLoading) {
        return (
            <StyledP>Loading...</StyledP>
        )
    }

    if (error) {
        return (
            <StyledP>Sorry an error occurred with the following message: {error}</StyledP>
        )
    }
    
    //...
}

Now we need to add the corresponding stories.

.add('Loading state', () => (
        <FilteredList items={[]} changeMode={() => void 0} isLoading />
))
.add('Error state', () => (
    <FilteredList items={[]} changeMode={() => void 0} error="Internal server error"/>
));

And now our Storybook looks like:

Screenshot from 2018-09-13 18-45-21
Screenshot from 2018-09-13 18-45-23

This example is rather simple but it shows pretty well how we worked with Storybook. Each time you create new component behaviors you then create the corresponding stories.

Ain’t nobody got time for that?

In fact it takes time when you are coding but I feel like it is more like an investment.

When you develop your app aren’t you trying to make it easy to use? Then why not make it easy for developers to use your code?

And that’s where Storybook is helping you, now your components are easier to share, this leads to a better collaboration between developers and therefore to better component development practices shared inside the team.

This is very important because you are not the only user of your code, you might be working with a team or someone else will take over your project afterwards.

We all had this part in our project code that have been here for ages and no one really know how to deal with it, how to avoid that? Make it good the first time you code it! Seems obvious but still is right, and for that you can use Storybook to share your front end components and make perfect APIs! (or at least very good ones)

Final thoughts

In a way we are all trying to do reusable components – whether you are trying to do a library or not, you want other members of your team to be able to update/fix your code. It’s not easy to make perfect APIs for your components if you can not have a lot of user feedback and that’s why Storybook or any visual doc are so efficient. They improve greatly the vision others have of your component and help them modify it without breaking anything.


You liked this article? You'd probably be a good match for our ever-growing tech team at Theodo.

Join Us