How to Create Your Own React Component Library

Akilesh Rao
JavaScript in Plain English
9 min readJan 18, 2022

--

So we’ve already created and published packages for both Node.js and Angular in this series. Now it’s time to do it for React. In the case of Angular, we had an option to natively create a “library” project and then test it inside an “application” project. We were able to make changes to the library and see those changes inside the application just like a typical single-page web application. In the case of React, to test our components we’ll have to go for a third-party library. The library that we’ll be working with is called Storybook.

There’s also a video version of this available on YouTube.

Why storybook

Storybook provides you with an environment where you can create your components in isolation, with change detection just like you would in the case of a typical single-page application. It also provides a clean user interface that lets you view different variants of the component that you’re creating.

Project setup

So let’s quickly set up our project. Since we’ll be creating a React component library and not a React application, we won’t be using create-react-app. We’ll instead build the skeleton from scratch. So open up an empty folder inside VSCode and type npm init -y to initialize a new package. You’ll find a package.json file inside the folder. Now you need to install the following list of dependencies.

npm install --save-dev react react-dom typescript @types/react

We need to install react and react-dom to create our component’s UI and interact with the DOM. I’m also adding typescript and type declarations for React to use static type checking. You can skip typescript if you want to.

Since we’re building a React component, we also need to add react and react-DOM as peer dependencies in our package.json file. This will basically ensure that the consumer of this package is going to be a React application. So inside your package.json file, add a peer dependencies object.

Peer dependencies inside package.json

Once this is done, we’ll initialize typescript by typing in npx tsc --init

This should create a tsconfig.json file inside your project with some default options. You can replace these options with the following

{
"compilerOptions": {
"jsx": "react",
"target": "es2016",
"outDir": "dist",
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ES2020",
"allowSyntheticDefaultImports": true,
"moduleResolution": "Node",
"declaration": true,
"declarationDir": "dist"
},
"include": [
"stories"
]
}
  1. jsx: To work with JSX in our React component
  2. target: Changes which JS features are down leveled and which are left intact. For example, an arrow function () => this will be turned into an equivalent function expression if target is ES5 or lower.
  3. outDir: Files will be emitted into this directory after compilation.
  4. allowJs: Allows JS files to be imported inside our project.
  5. skipLibCheck: Skip type checking of declaration files.
  6. strict: A more robust type-checking which results in better overall correctness.
  7. forceConsistentCasingInFileNames: Will throw errors if there are inconsistencies in naming based on the file system’s case sensitivity
  8. module: Sets the type of the module (es6, es2020, commonjs, umd)
  9. allowSyntheticDefaultImports: This basically lets you import your module in a more convenient way, for instance you can use import React from 'react' instead of import * as React from 'react'; by setting this option to true.
  10. moduleResolution: You can set the module resolution strategy using this, which in our case is going to be Node.
  11. declaration: Generate .d.ts files for every TypeScript or JavaScript file inside your project.
  12. declarationDir: directory inside which the declaration files will be stored
  13. include: Files to be included for compilation

You can also add an exclude option which takes in an array of files/folders if you want them to not be included in the compilation process.

Now we can initialize Storybook by typing in npx sb init .

This command would not work inside an empty project. It looks inside our project dependencies and makes a calculated guess about the type of our project (in this case, React).

Once it’s done, inside your package.json file, you’ll find a few extra dependencies, and some new scripts to run/build Storybook. There will be two new folders inside your project. The .storybook folder will have your default storybook configuration and the stories folder will have some dummy stories that you can test.

Now inside your terminal, type in npm run storybook .(This script was added when you initialized Storybook). Storybook’s interactive UI will open up in your browser with all your dummy components.

Storybook playground

We only care about the button component here, so you can go ahead and delete everything else inside your stories folder.

Keep only button component files.

A story is the rendered state of your React component. So for each React component, we’ll have a corresponding story which gets rendered on the browser whenever you run Storybook. You can reactively make changes to these stories and play around with their controls to see how your button is going to look like inside an application. Once you have completely tested your component, you can then push the component to npm. These story files are not going to be included in the production build, so you don't have to worry about the bundle size.

So we have our Button.tsx file which is a React component.

This react component is a typical button with a set of props. This button has a corresponding story file called Button.stories.tsx.

Inside this story, you first import your Button react component. After that, you create a default export for your button with some configurations (title, component). The args or arguments option defines how a component should render by providing a default set of values for our props.

Below this default export, we have a series of named exports which are just different variants of this default button (You’ll need these to render your buttons on Storybook).

const Template: ComponentStory<typeof Button> = (args) => <Button {…args} />;

is just a fancy way of creating a template that can be used for each of our variants. So any args that you provide inside these variants will override the default ones.

Now if you go back to the Storybook playground on your browser, you’ll see the buttons rendered and a controls section at the bottom. This controls section lets you customize your component by tweaking around with the prop values.

You can see how easy it becomes to test components in isolation using Storybook. Now that we have created and tested our React component, it’s time to publish it.

Bundling your component

There are several tools that one could use to compile and bundle their project. Webpack is a fan favorite but is most commonly used for applications and not component libraries. Rollup on the other hand is perfect for our use case, so we’ll be using Rollup. Install these libraries and plugins.

npm i --save-dev rollup @rollup/plugin-node-resolve @rollup/plugin-babel rollup-plugin-uglify rollup-plugin-postcss rollup-plugin-typescript2 rollup-plugin-peer-deps-external

rollup — Main module bundler library.

@rollup/plugin-node-resolve — Used to locate third-party modules used inside our project (we don’t have any third-party modules in our app currently, but this might change in the future).

@rollup/plugin-babel— To integrate with babel.

rollup-plugin-uglify — To minify your final bundle.

rollup-plugin-postcss — Includes the CSS that we created as separate files in our bundle. It does this by generating minified CSS from the *.css files and includes them via the <head> tag wherever used in our components.

rollup-plugin-typescript2 — This plugin compiles the TypeScript code to JavaScript for our final bundle and generates the type declarations for the types key in package.json.

rollup-plugin-peer-deps-external — This plugin will externalize our peerDependencies (react and react-dom in our case) in the final bundle as these will be provided by the consumer application.

This is what’s great about rollup. You have a host of plugins that you can add to your project to do specific tasks without doing them yourself. You can check out all the plugins available on their GitHub repo.

After installation, inside your root directory, create a config file called rollup.config.js and copy the following code.

We simply export an object with a set of properties. There will be an input and output option. The input option is the entry point of your application. So inside our root directory, you’ll need to create this entry point file (index.js) which will basically export all the components from our component library.

import { Button } from './stories/Button'export { Button }

The output key in your rollup config file is going to be the production bundle that gets generated after compilation. So when you build your component, after completion you’ll find a dist folder inside which there’s gonna be an index.js, which essentially is our component library.

In the end, we pass all the plugins that we installed in the plugins array.

Once this is done, you need to go to your package.json file. Inside the scripts object, add the following script buildLib: "rollup -c” . This command will look for a config file inside your project and run the compilation and bundling process based on the options provided inside the file. You also need to change the main key inside your package.json file to “dist/index.js” if you haven't done that already.

Now inside your terminal, run the buildLib command npm run buildLib. Once it’s done, you’ll now have a dist folder inside your project which will have an index.js file. You’ll be publishing this file to the npm store in the next and final step.

Publishing your component to npm

Before publishing your component to the store, there are a couple of steps that you’ll need to follow. All these steps have already been covered in the previous blog post which you can find over here or this video over here.

  1. Create and login into your npm account.
    You’ll need to create an npm account first. After registration, you’ll need to log in to your npm account inside VSCode. So inside your terminal, type “npm login”. It will ask for the same set of credentials that you filled in when registering your account. Once this is done, you can move on to the next step.
  2. Add a scope to the package.
    NPM will not allow two packages to have the same or even a similar name. So you’ll need to add a scope to your package which will make your package name unique. The scope, in this case, is going to be your username. So inside the package.json file, the name key is currently set to react-component-library. Change it to @“you-user-name”/react-component-library. So since my npm username is “akileshrao19”, my package name will be @akileshrao19/react-component-library. Again, the reason behind doing this is explained in detail in the previous blog post.

Once you’re done with these steps, you can finally publish it to the store npm publish --access=public

Packages with a scope in their name are private by default and to publish private packages you need a paid monthly subscription. So instead, we’ll publish it publicly, by adding the “access=public” flag.

If you followed along and did everything correctly, you’ll get a successful response which may look something like this

+@YOUR-USERNAME/react-component-library@1.0.0

You can go to npm’s website and log in. Inside your packages section, you’ll find your newly created React component. You can test out this component by importing it inside a React application as you’d normally do in the case of third-party packages.

Conclusion

With that, you’ve successfully created, tested, and published our React component to the npm store. You can access the codebase for this project from its Github repo.

A video version for this tutorial is also available on Youtube.

You can also visit the below links to see how to publish node packages or Angular components to the npm store.

Create your Angular component library(BLOG POST)
Create your Angular component library(VIDEO)

Create your NPM module(BLOG POST)
Create your NPM module(VIDEO)

If you have any queries or suggestions, you can put them in the comments or get in touch with me on any one of my socials mentioned below. Cheers!

YouTube
LinkedIn
Twitter
GitHub

--

--