How To Build a React Component Library -- Part 1

How To Build a React Component Library -- Part 1

React Component Library

Featured on daily.dev

React is one of the most popular technologies available today. React is widely used to build user interfaces. React consists of components, which are small pieces of code that fill a certain part of the UI you’re building.

Component libraries are great resources when it comes to developing React-based applications. They allow you to group your components in a way that lets others in your team can explore them and pick & choose the ones they need.

Mind you, there are quite a lot of libraries out there already for you to re-use. But if you’re working on your own custom components then keeping them inside your own library is definitely what you’ll want to do.

So without any delay let's get started 🥳...

Create a React app

Let's just create a simple react app using npx and move to this newly created application:

$ npx create-react-app first-component-library
$ cd first-component-library

Create a component

Before starting the code, we need to perform the following steps,

  • Remove public folder(we will not be needing this)

  • Remove all files/folders from src folder.(Don't worry😨,we will write it from scratch)

Now, Create a components folder under src folder and create a component that you want to share with your team. Here I am creating a component <HelloWorld/>

import React from 'react';

function HelloWorld() {
    return <h1>Hello dev! This is my first component library</h1>
}

export default HelloWorld;

Now, let's create our main file, index.js. and place it under the src folder, and export the component that we have just created from it. All the components that need to be shared should be exported from index.js, this will be a bootstrap point for our library.

export { default as HelloWorld } from './components/HelloWorld';

At this stage your folder structure should look something like this:

-- src/
   |-- components/
       |-- HelloWorld.js
   |-- index.js
-- package.json

Configure Rollup.js & Babel to Build Our Own Library

We have the first component(HelloWorld) of our library good to go. Now, to build our library, we need to install Rollup.js, Babel, and other packages that we require to bundle our library.

  • Let's install the following packages
$ npm i -D @babel/cli @babel/core @babel/preset-env @babel/preset-react rollup @rollup/plugin-babel rollup-plugin-delete rollup-plugin-peer-deps-external npm-run-all

Now let's talk about each one in short.

Babel:

Babel is a JavaScript transpiler that converts edge JavaScript into plain old ES5 JavaScript that can run in any browser (even the old ones). This is what @babel/preset-react and @babel/preset-env do in this order. They both depend on the @babel/core module being installed, so we need that too.

Rollup:

Rollup is a module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. In our case, it will compile all our components and creates a ./dist folder to put compiled code as an output.

rollup-plugin-delete:

This plugin is useful when you want to clean the ./dist folder before bundling.

rollup/plugin-babel:

Seamless integration between Rollup and Babel.

rollup-plugin-peer-deps-external:

Automatically externalize peerDependencies in a rollup bundle.

npm-run-all:

Allows us to run multiple npm-scripts at the same time.

  • Next, create a .babelrc file in the root folder. It should look like this:
 {
    "presets": ["@babel/env", "@babel/preset-react"]
}

These presets contain plugins that babel will utilize when converting the library from ES6 and JSX to a lower javascript versions.

  • Next, create a rollup.config.js file in the root folder. It should look like this:
import babel from '@rollup/plugin-babel';
import external from 'rollup-plugin-peer-deps-external';
import delete from 'rollup-plugin-delete';
import package from './package.json';

export default {
    input: package.source,
    output: [
        { file: package.main, format: 'cjs' },
        { file: package.module, format: 'esm' }
    ],
    plugins: [
        external(),
        babel({
            exclude: 'node_modules/**'
        }),
        delete({ targets: ['dist/*'] }),
    ],
    external: Object.keys(package.peerDependencies || {}),
};

Let's understand each configuration field:

input:

The entry point to the component(s) we want to bundle. We are pointing directly to index.js which we have used to export all the components.

output:

This specifies the directory where you want to save the bundled library. We are importing the output paths from package.json, which we will specify as the ./dist folder.

plugins:

This specifies all the plugins you wish to use and their respective configurations.

The folder structure at the end of this step should look like this

-- src/
   |-- components/
       |-- HelloWorld.js
   |-- index.js
-- .babelrc
-- rollup.config.js
-- package.json

Configure package.json

Our package.json looks like this right now:

{
  "name": "first-component-library",
  "version": "0.1.0",
  "private": false,
  "main": "dist/index.cjs.js",
  "module": "dist/index.esm.js",
  "source": "src/index.js",
  "dependencies": {
    "@babel/cli": "^7.13.16",
    "@babel/core": "^7.14.0",
    "@babel/preset-env": "^7.14.1",
    "@babel/preset-react": "^7.13.13",
    "@rollup/plugin-babel": "^5.3.0",
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "npm-run-all": "^4.1.5",
    "react-scripts": "4.0.3",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "rollup": "^2.47.0",
    "rollup-plugin-delete": "^2.0.0",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "web-vitals": "^1.0.1"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

We have to make a few modifications to this:

  • Add source, main & module values in package.json for our rollup.config.js
 {
    "name": "first-component-library",
    "version": "0.1.0",
    "private": false,
    "main": "dist/index.cjs.js",
    "module": "dist/index.esm.js",
    "source": "src/index.js",
  ...
}
  • Configure scripts Lastly, we need to add two scripts to our package.json which will compile the components into a bundle using the configurations in rollup.config.js .
 ...
  "scripts": {
    "build": "rollup -c",
    "build-watch": "rollup -c -w",
    "i-all": "npm i",
  },
  ...
  • Our new package.json should look like this:
 {
    "name": "first-component-library",
    "version": "0.1.0",
    "private": false,
    "main": "dist/index.cjs.js",
    "module": "dist/index.esm.js",
    "source": "src/index.js",
    "dependencies": {
        "@testing-library/jest-dom": "^4.2.4",
        "@testing-library/react": "^9.5.0",
        "@testing-library/user-event": "^7.2.1",
        "react-scripts": "3.4.3",
    },
    "scripts": {
        "build": "rollup -c",
        "build-watch": "rollup -c -w",
        "i-all": "npm i",
    },
    "eslintConfig": {
        "extends": "react-app"
    },
    "browserslist": {
        "production": [
            ">0.2%",
            "not dead",
            "not op_mini all"
        ],
        "development": [
            "last 1 chrome version",
            "last 1 firefox version",
            "last 1 safari version"
        ]
    },
    "devDependencies": {
        "@babel/cli": "^7.10.5",
        "@babel/core": "^7.11.4",
        "@babel/preset-env": "^7.11.0",
        "@babel/preset-react": "^7.10.4",
        "@rollup/plugin-babel": "^5.2.0",
        "npm-run-all": "^4.1.5",
        "react": "^16.13.1",
        "react-dom": "^16.13.1",
        "rollup": "^2.26.4",
        "rollup-plugin-delete": "^2.0.0",
        "rollup-plugin-peer-deps-external": "^2.2.3"
    },
}
  • Let’s build our library! Now, run the following commands:
$ npm update && npm run build

After this command, You should have a ./dist folder with two bundled files in it, index.cs.js and index.esm.js

You have successfully created a component library!💃 🕺 Dancing

In this article, we have created react UI component library, in the upcoming article, I will be explaining how to test this component library and publish it to a registry(like npm). So follow me, and stay tuned for more feature updates! ✌️.