How to Build a React component library?
Tags: react, javascript, package, opensource
Published: 2023-01-12 14:15:45

In this article I tried to talk the steps of create a react component library.

## Why do we need to build a react component library?
If you have several components that are shared between your projects or some js code that is copy-pasting and these things happen more than 2 or 3 times then you need to convert your shared code to a library.

So let's create a react component library.

First, we need to generate our component. We can create a React project and create a component. Our folder structure would be like this:
```
├── src
│   ├── components
|   │   ├── Button
|   |   │   ├── Button.tsx
|   |   │   ├── Button.scss
|   |   │   └── index.ts
|   │   ├── TextField
|   |   │   ├── TextField.tsx
|   |   │   ├── TextField.scss
|   |   │   └── index.ts
|   │   └── index.ts
├── package.json
├── tsconfig.json
└── rollup.config.js
```

src/components/index.ts
```
export { default as Button } from "./Button";
export { default as TextField } from "./TextField";
```
After we create our component we need to config our rollup. We use rollup to bundle our library(also we could use webpack).

Let's config our rollup to bundle our library:

rollup.config.js
```
import externalDeps from 'rollup-plugin-peer-deps-external';
import typescript from '@rollup/plugin-typescript';
import sourcemaps from 'rollup-plugin-sourcemaps';
import resolve from '@rollup/plugin-node-resolve';
import commonJS from 'rollup-plugin-commonjs';
import replace from '@rollup/plugin-replace';
import styles from 'rollup-plugin-styles';
import babel from 'rollup-plugin-babel';
import json from '@rollup/plugin-json';
import pkg from './package.json';

const globals = {
  react: 'React',
  'react-dom': 'ReactDOM',
  'react-query': 'ReactQuery',
};

const plugins = [
  externalDeps(), //prevents packages listed in peerDependencies from being bundled with our component library
  commonJS(), //convert common js modules to es6
  resolve(), // Locate and bundle third-party dependencies in node_modules
  typescript(), // transpiles our TypeScript code into JavaScript.
postcss() // transforms our Sass into CSS
];

export default {
  input: `./src/components/index.ts`,
  output: [
    {
      name: 'index',
      file: `lib/index.cj.js`,
      format: 'cjs',
      sourcemap: true,
    },
    {
      name: 'index' ,
      file: `lib/index.es.js`,
      format: 'esm',
      sourcemap: true,
    },
    {
      name: 'index',
      file: `lib/index.umd.js`,
      format: 'umd',
      sourcemap: true,
      globals,
    },
  ],
  plugins,
}));
```
**esm vs common js vs umd**
These types are js module formats.
Some tools like webpack and node use common js format and some tools like rollup and webpack2 use esm module. umd(Universal Module Definition) is the type of module that strive to work everywhere. Also, umd is a kind of fallback for other formats. If you want to include any of these files in the browser as standalone script tags without a build step like e.g. Webpack, you will want to use umd.

**How to test our packages?**
we have 3 options to test our package before publishing:
1. npm link
2. npm pack
3. Storybook

npm link:
npm link add our package in node_module universal of your system and install your package globally. every time we change our files this package will be updated in node_modules folder.

npm pack:
This command will pack our package and we can install the package in our project. with every change that we make in our package, we need to run this command and install our library again.

Storybook:
In my opinion, the best way to test our package locally is Storybook. 
In addition, to test our package, we can also have good documentation.

**Final step:**
Finally, we should publish our package.
with `npm publish` we can publish our package in the npm registry but before that, we should create an account in npm.
also, we need to make some changes in our packge.json file.

package.json:
```
{
  "name": "@react-components-library/react-sample-components-library",
  "version": "0.0.20",
  "main": "lib/index.js", // common js 
  "module": "lib/index.es.js", //esm
  "browser": "lib/index.umd.js", //umd
  "files": [
    "/lib"
  ],
  "type": "module",
  "scripts": {
    "test": "jest",
    "start": "styleguidist server",
    "build": "rm -rf ./lib && rollup -c",
    "prepublishOnly": "rm -rf lib && npm run build",
    "postbuild": "npm pack && tar -xvzf *.tgz && rm -rf package *.tgz",
    "docs:build": "styleguidist build"
  },
  "peerDependencies": {
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
...
}
```
There are some tips here:
1. we should our react and react-dom in our peerDependencies to prevent rollup to add these packages in our bundle.
2. when `npm publish` execute, first run `prepublishOnly` script. so we can add our build steps to this script.
3. `files` indicates which files or folders should push to the npm registry.