Building a React Components Library
Part 1 — The library itself and local development setup
Sharing is caring. In software, sharing also leads to… decoupling!
- Have you ever had your codebase flooded with so many components that they were basically screaming to be extracted?
- Do you have an idea for a cool UI library or want to share your components across multiple projects?
- Or do you just want to learn how to create component libraries and publish them to npm?
All these cases are covered in this tutorial. It will be a series of four pieces, each covering a different part of the process.
- Part one- Setting up the project for local development
- Part two- Testing
- Part three- Configuring the module bundler and publishing the first version of our library to npm
- Part four- Adding CI to deploying documentation automatically
Note: Stay tuned for the next pieces. As soon as they’re out, the list will be updated with corresponding links.
We will build it from scratch, from bare bones. It’s far easier than it sounds!
Let’s take a look at the complete list of the technologies we will use in the project:
Preparing the Project
We are starting from bare-bones, so let’s set up some basics to create a skeleton for the project.
First, create a directory for the library. Go inside and initialize the npm package:
npm init -y
I told you we are building it from scratch! By running
npm init with a
package.json is created without answering all those boring setup questions. The package name will default to the directory name.
Core dependencies: React and Emotion
We will use React and Emotion in our library, so let’s install and add them as
peer? It tells
npm/yarn about the compatibility of our dependencies and also helps us later with preventing them from being bundled into the final package. Bundling the whole
React into your library is a pretty bad idea, believe me.
Let’s run the following command to install the core dependencies:
npm install --save-dev react react-dom @emotion/core @emotion/styled
Later, manually update the
peerDependencies, replacing versions of the libraries to match the major one. Our
package.json should now look like this:
Live development with Styleguidist
Let’s add Styleguidist as we will be using it for the development with live reloading and preview of the components:
npm install --save-dev react-styleguidist webpack
To make Styleguidist work, we need to install Babel with some presets and create config files for both Babel and Styleguidist.
To install all Babel-required packages, run:
npm install --save-dev \
Now, we need to create the Babel config file,
.babelrc, in the root directory of the project. Let’s add the installed presets there:
The final step is to set up Styleguidist’s internal
webpack instance to use our
Babel config for loading the components properly.
styleguide.config.js with the following content:
We’re ready to launch the live development server now!
”start”: “styleguidist server” script to your
package.json and run
http://localhost:6060 and you should see something like this:
It’s running! But wait, it’s empty…
We have the skeleton of the project and development environment fully set up, time to create some components.
The sources of the components will be laid out in the
src/components directory. Pretty straightforward.
Let’s create the following structure in the project:
We will start by making a really simple button.
Copy the following code to
http://localhost:6060 again and… drum roll … we see that the button has been found!
It doesn’t show anything yet, but Styleguidist makes it easy to preview and describe the components.
Let’s add the
Button.md markdown file next to the
Button.js and, inside, type:
After adding the
Button.md let’s restart Styleguidist
(npm start) and refresh the docs (only needed when adding
.md files, it’s hot-reloaded otherwise), we should now see:
We have created our first component, have a development environment with live preview, and the export-ready documentation at the same time.
Let’s Add Some Style
Our button is plain and boring. Writing in plain CSS is not convenient; let’s use Emotion to have some CSS-in-JS capabilities to write styles easily.
Note: If you want to use component selectors, you need to install a
Babel plugin to handle the
Emotion styles preprocessing.
To do it, run
npm install --save-dev babel-plugin-emotion and add the plugin to
Let’s sprinkle some CSS on the button now:
My name is Button, The Button.
Adding More Components and Utilities
When the library grows, it’s better to share common parts between the components.
Let’s extract some styles from the
Button component and put them in a separate file:
To create CSS that can be shared across components, we will use the
css function from Emotion.
We may now import and add the styles to the button:
We improved one thing, but now another went bad…
from '../config/styles'? Imagine having more levels of nesting…
Let’s change relative imports to absolute ones.
Babel comes to the rescue once again with a module resolver plugin.
Let’s install it with
npm install babel-plugin-module-resolver and add it to plugins in
.babelrc like this:
Shoutout to my friend FatFisz (@GitHub) for contributing a lot to that plugin!
root option tells
Babel where to look for the modules.
It’s like specifying an additional
node_modules directory. Now, we can import things from
src. The ugly
Similarly, we can directly import components from other components.
For example, if we wanted to import
Button in a different component, we would do it like this (as the
components directory is a direct child of
import Button from 'components/Button';
We’ve created the React component library from scratch!
We’ve added Styleguidist to set up the development environment and Babel to transform our React code properly. Then we created our first component, styled it with Emotion, and extracted some shared CSS.
Finally, we improved the readability of the code by enabling absolute imports.
We’ve done a good job! That’s it for this part.
The full code is available in the GitHub repository. You can check out the
part1 tag (
git checkout part1) to see the full example for this part.