Build a Private React Component Library — Rollup, Material-UI & Testing Playground — Part I

I have always liked the idea of building code as lego-blocks — re-usable, modular, simple on their own but capable of great things when they come together.

Following that strategy/dream, I am going to create multiple component libraries (auth, utils, charts, etc.) that can be shared across the team to be used across multiple projects.

In this four part series, we are going to only focus on building an Auth Component Library.

  • Part I: Create a React Component Library with a simple<HelloWorld /> component, create a Testing Playground to consume our component and publish the library to a private Github Repository.
  • Part II: Publish your package to a private Github repository so your library stays within the organisation and your team members can pull it for other projects
  • Part III: Add Material-UI as a dependency to your component library, build a <SignUpForm /> and a <SignInForm />component as part of our Auth Component Library, consume that in our Testing Playground.
  • Part IV: Add a Mock-API to our Testing Playground and link that to our components.
Photo by HONG LIN on Unsplash

Thoughts on Create React Library?

I tried it, I spent a week trying to figure it out and although a great package to start trying this idea out, I felt like it was a truck to the sports car I wanted and it really slowed down development (I couldn’t use the shorthand for fragments for some reason!). Plus, it’s not being actively maintained anymore which was a hard pass for me.

Acknowledgments:

This article has been inspired from a TypeScript version of a similar tutorial, but I use plain JSX and so I have modified the tutorial to fit that, highlighted some pitfalls to avoid that I encountered and demonstrated how you can make a component library of complex components built using Material-UI.

1. Create a React app

In a directory of your choice, create a simple react app:

$ npx create-react-app auth-component-library

2. Create a HelloWorld Component

  1. Delete the public folder.
  2. Delete all the files inside the src folder.
  3. Create a components folder inside src folder and create a HelloWorld.js file inside the src/components folder.

4. Next, create an index.js file inside the src folder and export the component we just created. The index.js file will be our main file throughout this process, exporting all the components we will create inside the components folder.

5. The folder structure should now look like this:

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

3. Configure Rollup.js & Babel to Build Our Library

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

  1. We will be installing the following packages:
$ cd auth-component-library$ 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

Here’s a very short description about what each of them do:

  • Babel: It transpiles JSX in our files 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 of our components and put the compiled code in a ./dist folder.
  • rollup-plugin-delete: A plugin that will take care of the garbage between each build (clean the ./dist folder).
  • rollup/plugin-babel: Integrates between Rollup and Babel.
  • rollup-plugin-peer-deps-external: Our component library will classify React, ReactDOM (and Material UI in the future) as peerDependencies because we will expect the calling-app to already have these dependencies installed. This plugin will prevent these peerDependencies from being included in the bundle.
  • npm-run-all: allows us to run multiple npm-scripts at the same time

2. Next, create a .babelrc file in the root folder. It should look like this:

.babelrc in the root folder

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

3. Next, create a rollup.config.js file in the root folder. It should look like this:

rollup.config.js in the root folder

This is what each configuration field stands for:

  • 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 the component.
  • output: This specifies the directory where you want to save the bundled library. We are importing the output paths from package.json (More about this in the next step), which we will specify as the ./dist folder.
  • plugins: This specifies all the plugins you wish to use and their respective configurations. For instance, external is asking rollup to exclude peerDependencies as part of the bundle as they will be imported separately by the app calling this package. We will also configure peerDependencies in the next step.

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

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

4. Configure package.json

My package.json looks like this right now:

Initial package.json

We have to make a few modifications to this:

  1. Add source, main & module values in package.json for our rollup.config.js
{
"name": "auth-component-library",
"version": "0.1.0",
"private": true,
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"source": "src/index.js",
...
}

2. Add react, react-dom to peerDependencies and remove them from dependencies .

{    "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"
},
"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",
"rollup": "^2.26.4",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-peer-deps-external": "^2.2.3"
},
"peerDependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
}

Do not add react and react-dom to devDependencies as that will give us an Invalid Hook Call error when we set-up our “playground” in the next step.

3. 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 . Don’t worry about the playground scripts, just add them, they’ll make more sense in the next step when we set up our “Testing Playground”.

{
...
"scripts": {
"build": "rollup -c",
"build-watch": "rollup -c -w",
"start-playground": "cd playground && npm run start",
"i-all": "npm i && cd playground && npm i",
"dev": "npm-run-all --parallel build-watch start-playground"
},
...
}

4. The new package.json should look like this:

Version 2: package.json

5. Let’s build our library!

Now, run the following commands:

$ npm update && npm run build

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! Now, we build our Testing Playground.

5. Add a Testing Playground to Our Project

We are now going to create a “playground” in our app so we can test our components as we develop them.

  1. For this, we are going to create another react app, this time inside the app we have already created.
$ cd auth-component-library
$ npx create-react-app playground

2. Add the auth component library you just created as a dependency in playground/package.json

{
"name": "playground",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.3",
"auth-component-library": "file:.."
},
...
}

3. Modify playground/src/App.js to call the <HelloWorld /> component

playground/src/App.js

4. Run the playground app:

$ npm run i-all
$ npm run dev

This will build our library, watch for modifications, and run the Playground App at http://localhost:3000 . You should now see “Hello World” on your screen in giant letters. It’s a small but meaningful victory!

You can also modify the <HelloWorld /> component and your screen should update automatically.

Wrapping-Up..

That’s it for Part I. We’ll pick it up in Part II by publishing this into our private Github Repository. Please let me know if I missed anything.

For the love of reusability.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store