Fastest way to create a React library
Tags: webdev, react, javascript, npm
Published: 2023-12-01 09:27:13


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/chxq7nhacwy7h8rihqid.png)

So you are a React developer, and you've got a wonderful idea 💡, which you want to share with the React open source universe? The challenge starts when you search for templates or any other bootstrapping tools for this goal. Site and app developers have a variety of choices: Next.js, Gatsby, Electron. But we, library devs, are getting almost none of this. 

From my experience developing an internal design library for a huge e-commerce company, I came up with a list of features React library setup has to support:

- CommonJS and ES Modules bundling;
- Typescript support and proper declaration emitting;
- Various style architectures support (CSS modules, Styled Components, Tailwind etc.);
- Automated code quality checks for code and style;
- Unit testing;
- Documentation and component playground tool — Storybook;
- CI/CD setup to check PRs and do releases.

This set of features covers such use cases from creating a small hook package to maintaining a huge React components design library. So that's how I came with [React Library Template](https://github.com/morewings/react-library-template).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oacvav070wvs2t0b9gdu.png)

**React Library Template** — is a repository which contains all required bootstrap configuration to quickstart library development using most modern tools. You don't have to spend hours configuring and fixing various problems, just clone the repo and start creating.

## Set up

You need to have **Node** >=18.x and **pnpm** installed. I've chosen pnpm because it works multiple times faster than yarn. The easiest way to install pnpm is to run this command:
```shell
corepack prepare pnpm@latest --activate
```

Then you can manually clone the repo or use **degit**

```shell
git clone git@github.com:morewings/react-library-template.git my-lybrary
# or
npx degit https://github.com/morewings/react-library-template my-library
# then 
cd ./my-library
pnpm i
```
You can also use GitHub templates functionality.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/497agzh0qtov5hgru88c.png)

## Development

The library code is inside `./src/lib` folder. `./src/lib/index.ts` is used as an entry point for bundling (more on this later).

For small and **simple modules**, you can develop using only **vite** hot reload functionality. Run `pnpm run dev` in the project, and you will be able to see your changes live in the browser: http://localhost:5173/ You change this development environment at `./src/env/App/App.tsx`. Imports from `./src/env/` folder are forbidden in the `./src/lib/` folder. 

For bigger and more complex packages like **design libraries**, you can use Storybook to have a live preview. 

1. Create a component skeleton with Storybook story files. There is a working example in `./src/lib/CounterDemo`.
2. Run Storybook in development mode: `pnpm run start:docs`
3. Navigate to component preview page, e.g., http://localhost:6006/iframe.html?args=&id=example-counter--example-counter&viewMode=story

In both cases, you will be able to see and debug your changes live in browser without page reload.

## Styling

By default, template is equipped with **CSS Modules** architecture. It allows your component styles to have unique CSS class names, thus reducing risk of collisions.

```jsx
import classes from './Component.module.css';
//...
const Component = () => {
    return <div className={classes.wrapper}>{/*...*/}</div>
}
```

Style processing is done with **postcss** package. Configuration is available at `./vite.config.ts` file.

```js
import {defineConfig} from 'vite';
import postcssPresetEnv from 'postcss-preset-env';

// https://vitejs.dev/config/
export default defineConfig({
  // Here you can extend postcss configuration
  css: {
    postcss: {
      plugins: [
        postcssPresetEnv({}), // add options if needed
      ],
    },
  },
});
```

**Styled components** and **Tailwind** examples will be added soon.

## Bundle

Vite is used to bundle the package. It was chosen because of the speed and wide compatibility (browser HMR and decent Rollup build).

Run `pnpm run build` to get your code compiled to `dist` folder. There are two different bundles created:

- `./dist/index.umd.cjs` contains UMD for older set-ups.
- `./dist/index.js` contains ES Modules bundle which supports tree-shaking and other fancy features.

Rolled up type definitions are bundled to `./dist/index.d.ts`. And styles to `./dist/style.css`.

Here is corresponding `./package.json` config:

```json
{
  //...
  "type": "module",
  "files": ["dist", "README.md"],
  "main": "./dist/index.umd.cjs",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "sideEffects": false,
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.umd.cjs"
    }
  }
  //...
}
```

## Unit testing

You can write unit tests using **Jest** and **React Testing Library**. Example tests are available here: `./src/lib/CounterDemo/Counter.spec.tsx`. Test configuration is done in `./jest.config.ts` and `./src/setupTests.ts` files.

```shell
# runs unit tests in dev mode
pnpm run test --watch
```

## Automated code quality checks

Code quality is important for any library. In the template we have set checks using ESLint, Stylelint and type compliance check.

### ESLint

ESLint checks are available using these commands:

```shell
# checks code and emits errors and warning
pnpm run lint:code

# checks code and also tries to fix errors
pnpm run fix:code
```
 
You can see linter configuration at `./.eslintrc.cjs`.


### Stylelint

Stylelint checks are available using these commands:

```shell
# checks style files and emits errors and warning
pnpm run lint:style

# checks style and also tries to fix errors
pnpm run fix:style
```

You can see linter configuration at `./.stylelintrc`.

### Types

You can check types using this command:

```shell
# checks project for type errors
pnpm run lint:types
```

## Commit hooks

The template has **husky** (`./.husky/`) and **lint-staged** (`./.lintstagedrc`) configs included. Thus, each file you commit is checked by the corresponding linter. Also, we run unit tests before push. 


## CI/CD

`./.github/workflows/pull-request-jobs.yml` is responsible for pull requests checks. This flow runs every time you open PR to `master` branch.

`./.github/workflows/pages.yml` is responsible for deployment your Storybook to Gitlab pages. It runs when you add commits to `master` branch. You have to set up repository accordingly to make it work. The action expects Storybook pages being built to `./storybook-static` folder.

```shell
# builds Storybook
pnpm run build:docs
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bogy0g00kvogiyamnx9x.png)


`.github/workflows/merge-jobs.yml` is responsible for package publication. Runs on`master` branch.

## Releasing your changes

Don't forget to change package name in `./package.json`

```json
{
  "name": "your-library-name",
  "homepage": "your-home-page",
  "private": false,
 //...
}
```

`JS-DevTools/npm-publish` GitHub action is responsible for changes release. To set up this action, you need to [obtain an access token](https://docs.npmjs.com/about-access-tokens) from NPM registry. And save it as repository secret under the name `NPM_TOKEN`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/39r54f3v1zsybbrui4ws.png)

In order to release, you have to run this command on `master` branch:

```shell
# patch => 0.0.x
# minor => 0.x.0
# major => x.0.0
pnpm version patch|minor|major
```

Then push your changes to GitHub.

Congrats, your contribution is done.