Creating and testing a react package with CRA and rollup
Tags: react, rollup, opensource, createreactapp
Published: 2020-03-14 11:47:30

---
title: Creating and testing a react package with CRA and rollup
published: true
description: Create a react package from scratch or an existing component using rollup
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/jlbsautiq8awfwq7u0vl.png
tags: react, rollup, OpenSource, CreateReactApp
---


#### TL;DR
check out the complete setup in this [repo](https://github.com/codenaz/create-react-package-tutorial)

## Introduction

This article assumes that you have at least a beginner level knowledge of react. It is not intended to teach you react.

You may create a nice component in your app that you see a use case for in other apps. You could also have an idea for a component that you would like to make available to other developers. The problem usually is, how do you bundle this component and make it distributable. In this article, I will provide guidelines on how we can easily bundle our component with [rollup](https://rollupjs.org), test it locally and make it available for installation on npm.

## Setting up a project

We will be making use of [create react app](https://https://create-react-app.dev)(CRA) to setup react in this guide. We can either have CRA installed globally on our machine or we can use it directly via npx which ensures we are running the latest version. Running the following command sets up our project with the name 'react-dog':

```cli
npx create-react-app react-dog
```

## Project structure

We will create a folder named `lib` under the `src` folder. This is where all the source code for the component or library will be. In the `lib` folder we will have the following files:

- the styles (dog-style.css)
```css
.dog {
  display: flex;
  max-width: 100px;
}
```

- an image (dog-image.jpg)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1fdi69hc20maka87jee4.jpeg)

- the main component file (Dog.js)
```react
import React from 'react';
import dog from './dog-image.jpeg';
import './dog-style.css';

export default function Dog() {
  return (
    <div class='dog'>
      <img alt='dog' src={dog} />
    </div>
  );
}
```

Our folder structure will end up looking like this:

![Folder structure](https://dev-to-uploads.s3.amazonaws.com/i/5o686x0nor34370d2i4q.png)

## First Test Run

To ensure that there are no errors and our package is working like it is supposed to before bundling, you should import your component or `Dog` in our case into App.js and run this project. Replace the content of App.js with this:

```react
import React from 'react';
import Dog from './lib/Dog.js';

function App() {
  return (
    <div className='app'>
      <Dog />
    </div>
  );
}

export default App
```

Run the project with `npm start` and everything should run smoothly before you proceed to the next step

## Setting up rollup and babel

Run this command to install rollup and all the plugins we require to bundle our package.

```cli
npm i -D rollup rollup-plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-image rollup-plugin-peer-deps-external rollup-plugin-postcss rollup-plugin-visualizer
```
Here is a brief description of what each plugin does:

* [rollup-plugin-babel](https://github.com/rollup/rollup-plugin-babel): This integrates rollup with babel.

* [@rollup/plugin-commonjs](https://github.com/rollup/plugins/tree/master/packages/commonjs): Converts any commonjs module to ES6.

* [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve): Locates third party modules in `node_modules`

* [@rollup/plugin-image](https://github.com/rollup/plugins/tree/master/packages/image):Imports your images and svg icons.

* [rollup-plugin-peer-deps-external](https://github.com/pmowrer/rollup-plugin-peer-deps-external): Externalize dependencies in a rollup bundle. This is automatic for peerDependencies.

* [rollup-plugin-postcss](https://github.com/egoist/rollup-plugin-postcss): Transforms styles with js plugins. You need this if your package contains styles

* [rollup-plugin-visualizer](https://github.com/btd/rollup-plugin-visualizer): Visualize and analyze your Rollup bundle to see which modules are taking up space.

Run this to install babel and the babel plugins needed for your compilation

```cli
npm i -D @babel/cli @babel/core @babel/preset-env @babel/preset-react
```


## Configurations

Create a babel configuration file `.babelrc` in the root of your project and insert the following content:

```babel
{
  "presets": ["@babel/env", "@babel/preset-react"]
}
```

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

Next, we need to configure package.json by adding fields that will point it to the entry point of your package after bundling. We will be adding both a `main` and a `module` field. The main field makes sure that Node users using require will be served the CJS version. The module field is not an official npm feature but it will enable ES6 aware tools to make use of an ES6 version of your library. So, we are generating two builds. Modify your package.json by adding the following fields:

```json
{
...
  "main": "dist/index.cjs.js",
  "module": "dist/index.esm.js",
}
```

The values of `main` and `module` above indicate that the bundles will be generated in a dist folder. You can also add a files field to specify what must be published to npm. Files like `LICENSE`, `CHANGELOG.md` and `README.md` are published by default. 

```json
...
"files": [ "dist/*" ]
```

The above setting specifies that all files in the dist folder must be published to npm.

Move `react` and `react-dom` from the dependencies field to `devDependencies`. Specify the minimum version of `react` and `reactDom` that your package requires in the `peerDependencies` field. I am using `>=16.8.0` in this example because it is the earliest version with support for hooks. At this point, your `dependencies` field should be empty or deleted while your `peerDepedencies` and `devDependencies` fields should look similar to this.

```json
{
...
  "peerDependencies": {
    "react": ">=16.8.0",
    "react-dom": ">=16.8.0"
  },
  "devDependencies": {
    "@babel/cli": "^7.8.4",
    "@babel/core": "^7.8.7",
    "@babel/preset-env": "^7.8.7",
    "@babel/preset-react": "^7.8.3",
    "@rollup/plugin-commonjs": "^11.0.2",
    "@rollup/plugin-image": "^2.0.4",
    "@rollup/plugin-node-resolve": "^7.1.1",
    "react": "^16.13.0",
    "react-dom": "^16.13.0",
    "react-scripts": "3.4.0",
    "rollup": "^2.0.6",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-peer-deps-external": "^2.2.2",
    "rollup-plugin-postcss": "^2.4.1",
    "rollup-plugin-visualizer": "^3.3.1"
  }
  ...
}
```
Next, create a file named `rollup.config.js` in the project's root directory. This is where all the configurations for `rollup` will be specified. Insert the following content into `rollup.config.js`:

```javascript
import babel from 'rollup-plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
import resolve from '@rollup/plugin-node-resolve';
import image from '@rollup/plugin-image'
import visualizer from 'rollup-plugin-visualizer';
import pkg from './package.json';

export default {
  input: './src/lib/Dog.js',
  output: [
    {
      file: pkg.main,
      format: 'cjs'
    },
    {
      file: pkg.module,
      format: 'esm'
    }
  ],
  plugins: [
    external(),
    postcss(),
    babel({
      exclude: 'node_modules/**'
    }),
    resolve(),
    commonjs(),
    image(),
    visualizer()
  ]
};

```

This is what each configuration field stands for:

* input: The entry point to the component you want to bundle. In this article, I am pointing directly to `Dog.js` but it is common to find projects where a main file like `index.js` file is created to export the component(s)

* output: This specifies the directory where you want to save the bundled library. With rollup, you can specify an array of multiple outputs in different formats and directories. We are importing the output paths from package.json

* plugins: This specifies all the plugins you wish to use and their respective configurations. You can look up documentation on each plugin if there is a need to configure them differently.

## Bundling

After setting up and configuring our tools, the next step will be to create a bundle that will be distributed through npm or any other package manager of your choice.  

Running `rollup -c` compiles the component into a bundle using the configurations in `rollup.config.js`. We will modify our build script in package.json so that we can execute `npm run build` whenever we want to generate a new bundle. 

```json
...
build: "rollup -c"
```

Run `npm run build` and you should have a dist folder with two bundled files in it. If you added the `rollup-plugin-visualizer`, you will also get a stats.html file that you can open in your browser to inspect the bundle size.


## Testing the bundle

Next, we should make sure our bundle works before publishing it on npm. One easy way to do this locally is to link the package to our global `node_modules`. This can be done by running `npm link` on the command line from the root of the project. Ensure you have specified the name of your package in `package.json` because that will be your package's name.

```json
name: "react-dog"
```

After running `npm link`, a symlink will be created for the dist folder in the global node_modules. 

Next, run `npm link react-dog`(replace 'react-dog' with your package name) from the root of the project. This will create another symlink between your global node_modules and your current local node_modules for `react-dog` so that you can simply import it in App.js like your other installed third-party libraries.

```react
import Dog from 'react-dog'
```  
From the script above, you can observe that we no longer import `Dog` from './lib/Dog.js'. Run `npm start` and we should see that the library is working as it should. Rebuild if you make any changes so that you can test the updated version of your library.

## Publishing

Now that everything is in place, we can prepare our package for publishing. Similar to .gitignore, it is advisable to create a .npmignore file that will contain all the files irrelevant to our publication. This helps cut down on the package size.

```.npmignore
src
rollup.*
.babelrc
.eslintrc
stats.html
```
I have excluded the src folder from the library because we are only using it for tests.

You can read more on including and excluding files [here](https://docs.npmjs.com/files/package.json#files).

If you aren't already logged in to npm, run `npm login` on the command line and follow the prompts to fill out your login details.

Finally, run `npm publish` and if everything goes well, your package will be available for installation from anywhere by simply running `npm install <package-name>`

## Troubleshooting

Some common problems you might encounter during this process include:

* <named import> is not exported by module: This mainly occurs with some packages in node_modules. While <named import> can be any named imports, you will have to specify it manually in the `commonjs` plugin added to your configuration in `rollup.config.js`. Example, for the following error: 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mqfykj4oilt64onk99p0.png)

a fix will be to add it to the `namedExports` field in the `commonjs` plugin like this:

```js
plugins: [
...
commonjs({
  namedExports: {
        'node_modules/react-is/index.js': ['isElement', 'isValidElementType']
      }
})
]
```

Read more on this error [here](https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module)

* React scripts requires a dependency(webpack): 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xj9ey0cufw6u0sonc0qx.png) 

If you get the error above when trying to run the project with `npm start`(react-scripts), install a webpack version that is compatible with your version of react-scripts as a devDependency. Don't worry about installing a wrong version, react-scripts will raise an error that informs you of the correct version.

```cli
npm run i -D webpack@version
```

* Cannot find module after linking package: After linking your package, if your application refuses to build or start when trying to build or test your bundle due to any missing module error, follow these steps to fix the issue:

1. Delete node_modules
2. Run `npm install`
3. Run `npm link <package>`

Try restarting or rebuilding the project.

## Conclusion
Rollup provides a very neat way of bundling javascript. It keeps the bundle relatively readable in case a user of your package needs to debug it in node_modules.

If you need access to the actual files for this setup, you can check the [repo](https://github.com/codenaz/create-react-package-tutorial)