author-image

Stephen Kelly

Create a component library using Create React App, Storybook and Jest

First Published 19 October 2019
(Last Updated 12 March 2021)

Let’s create a reusable UI library that can be shared across multiple projects. React components are perfect for this.

“Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.” – reactjs.org

We’ll build our UI following a Component-Driven Development (CDD) methodology.

Here at Fathom we use the create-react-app toolchain for React projects. It sets up your development environment so that you can use the latest JavaScript features and provides a nice developer experience.

Create react app requires you to have NodeJS and npm installed. When creating this post I used Node 14.15.5 npm 6.14.11. At time of writing, I am using create-react-app version 4.0.3 React version 17.0.1.

Let’s create a project, run:

# Pick a unique project name
npx create-react-app fathom-react-components
cd fathom-react-components
# Runs the frontend app on port 3000:
npm start

Create react app screen

Now that you have your project created, Let’s add Storybook and start to build components in isolation. At time of writing, storybook is at version 6.1.21

Setup Storybook using the automated command line tool. This command adds a set of boilerplate files for Storybook in your project:

cd fathom-react-components 
npx -p @storybook/cli sb init
# Starts the component explorer on port 9009:
npm run storybook

Storybook welcome screen

Storybook adds its example stories to a /stories folder. In this tutorial, we do not adopt that convention and simply rely on the .stories.js naming scheme.

Should you wish to adopt a different convention for how your stories are named or where they are stored, the configuration file .storybook/main.js allows you to control the conventions used.

You can delete the /stories directory provided by storybook.

Let’s create a simple button component and it’s story file src/components/Button.js and src/components/Button.stories.js.

import React from 'react';
import PropTypes from 'prop-types';

export default function Button({ label, backgroundColor, onClick }) {
  return (
    <button onClick={onClick}
    style={backgroundColor && { backgroundColor }}>
      {label}
    </button>
  );
}

Button.propTypes = {
  backgroundColor: PropTypes.string,
  label: PropTypes.string.isRequired,
  onClick: PropTypes.func,
};

Button.defaultProps = {
  backgroundColor: null,
  onClick: undefined,
};
// src/components/Button.stories.js
import React from 'react';

import Button from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
};

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

export const Default = Template.bind({});
Default.args = {
  label: 'My Button',
};

Once the files have been created, restarting the Storybook server should show us our Button component.

Storybook button

Snapshot Tests

Snapshot testing is a common type of regression testing, where an image of a UI or UI component is captured as a reference and then when code changes are made a new capture is compared to the reference. Storyshots is an official Storybook addon for snapshot testing. Now that we have our component set up, let’s set up some Snapshot testing using Storyshot and Jest. Note that Create React App will have already set up Jest.

With the Storyshots addon a snapshot test is created for each of the stories. Use it by adding a development dependency on the package:

npm i @storybook/addon-storyshots react-test-renderer

And create a src/storybook.test.js file with the following inside:

// src/storybook.test.js

import initStoryshots from '@storybook/addon-storyshots';
initStoryshots();

Once the above is done, we can run npm test and see the following output:

Jest example

Packaging Our Components

How do we take our component and package it to be used in other projects?

First let’s add components/index.js this will be the entry point to our library and where we export our components:

// components/index.js

import Button from './Button';

export { 
    Button
};

Then we need to add some additional dev dependencies:

npm i cross-env @babel/cli @babel/preset-env @babel/preset-react --save-dev

And enable preset-env and preset-react by adding it to our presets array inside babel.config.js:

// babel.config.js

module.exports = function (api) {
    api.cache(true);
    
    const presets = [ "@babel/preset-env", "@babel/preset-react" ];
    const plugins = [ "macros" ];

    return {
      presets,
      plugins
    };
}

To avoid any React conflict issues you can move the following React dependencies to peer dependencies, as the app using this library will have React installed. If you need a refresher on peer dependencies, see this Fathom blog post.

// package.json
"peerDependencies": {
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-scripts": "4.0.3",
    ...
}

To prepare for publishing, In your package.json add:

    "main": "dist/index.js",
    "private": false,
    "files": [ "dist", "README.md" ],
    "repository": {
        "type": "git",
        "url": "URL_OF_YOUR_REPOSITORY"
    }

We want Babel to compile our code from src/components and output it to the dist directory.

Let’s add the compile script:

// package.json

"clean": "rimraf dist",
"compile": "npm run clean && cross-env NODE_ENV=production babel src/components --out-dir dist --copy-files --ignore __tests__,spec.js,test.js,stories.js,__snapshots__"

Finally, To build your package run:

npm run compile

Now you have a package which you can publish to npm:

npm publish

When successful you can install your package via npm or yarn:

npm i fathom-react-components
yarn add fathom-react-components

And import into your app:

import { Button } from 'fathom-react-components';

Conclusion

Congrats, you have created a React component library with Storybook and Jest. This is a great start but there is more to think about.

How do you style your components? How do you set up your git repository to manage versioning? How do you automate deployment? etc.

These are questions we may explore in future posts, Check back with us so you don’t miss out.

SO WHAT DO YOU THINK ?

We love to talk about ideas, innovation, technology and just about anything else. Really, we love to talk.

Contact fathom
a