Develop a Full-Fledged Component Library with React, just like Material UI
Tags: react, javascript, webdev, programming
Published: 2022-05-01 06:13:26

Always wondered how **component libraries** work in **React**? Want to create a **library** of your own, but the task seems too daunting? Fret no more! This article will teach you just that!

Let's kick things off!

![roll-up-sleeve](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w0snkivb3s7wxnlmpzr4.gif)

## Initializing Project

Initialize a new project with

```bash
npm init
```

Add the dependencies using:

```bash
npm i react react-dom
```

Rename the `dependencies` in `package.json` to `peerDependencies`, which informs **npm** of the packages your project relies on.

## Adding Storybook

Now comes the most tedious part of the setup.

Since you would need to **test out the components** you build, you could **create a web app** with all the components or use a tool like **Storybook** to easily test out your components.

Initialize a **Storybook** project with

```bash
npx sb init
```

This will automatically detect the **project type**, add the necessary **packages** & **scripts**.

Move the `/src/stories` folder to the root of your project (`/stories`) and update `/.storybook/main.js` with:

```js
module.exports = {
  // ...
  stories: [
    "../stories/**/*.stories.mdx",
    "../stories/**/*.stories.@(js|jsx|ts|tsx)",
  ],
};
```

You can now start up the storybook project with

```bash
npm run storybook
```

To add **CSS Modules** support to the project, install the following:

```bash
npm i -D @storybook/addon-postcss storybook-css-modules-preset
```

Update the `/.storybook/main.js` configuration with:

```js
module.exports = {
  // ...
  addons: [
    // ...
    "@storybook/addon-postcss",
    "storybook-css-modules-preset",
  ],
};
```

**NOTE:** Noticed that **storybook**'s dependencies are conflicting with **React 18**, if you get an error while starting up the **storybook**, try downgrading to **React 17**.

## Create a Component

Now it's finally time to create a **component**.

```css
/* /src/Button/button.module.css */
.storybookButton {
  font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 700;
  border: 0;
  border-radius: 3em;
  cursor: pointer;
  display: inline-block;
  line-height: 1;
}

.storybookButtonPrimary {
  color: white;
  background-color: #1ea7fd;
}

.storybookButtonSecondary {
  color: #333;
  background-color: transparent;
  box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
}

.storybookButtonSmall {
  font-size: 12px;
  padding: 10px 16px;
}

.storybookButtonMedium {
  font-size: 14px;
  padding: 11px 20px;
}

.storybookButtonLarge {
  font-size: 16px;
  padding: 12px 24px;
}
```

```js
// /src/Button/Button.js
import React from "react";

import classes from "./button.module.css";

const Button = ({ primary, backgroundColor, size, label, ...props }) => {
  const mode = primary
    ? classes.storybookButtonPrimary
    : classes.storybookButtonSecondary;
  return (
    <button
      type="button"
      className={[
        classes.storybookButton,
        classes[`storybookButton${size}`],
        mode,
      ].join(" ")}
      style={backgroundColor && { backgroundColor }}
      {...props}
    >
      {label}
    </button>
  );
};

export default Button;
```

```js
// /src/Button/index.js
export { default } from "./Button";
```

Since we are working on a **component library**, it is crucial to **export** the **components** in the main **index.js** file.

```js
// /src/index.js
export { default as Button } from "./Button";
```

To test out the **component**, let's add a **story**. Make sure you remove the default **stories** that were added by **Storybook**.

```js
// /stories/Button.stories.js
import React from "react";

import { Button } from "../src";

export default {
  title: "Basics/Button",
  component: Button,
  argTypes: {
    backgroundColor: { control: "color" },
  },
};

const Template = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: "Button",
};

export const Secondary = Template.bind({});
Secondary.args = {
  label: "Button",
};

export const Large = Template.bind({});
Large.args = {
  size: "Large",
  label: "Button",
};

export const Small = Template.bind({});
Small.args = {
  size: "Small",
  label: "Button",
};
```

Now you can run **storybook** and visit `http://localhost:6006/?path=/story/basics-button--primary` to checkout & modify the component on the fly.

Feel free to add as many **components** and **stories** as your **library** requires!

## Building Project

What good is a project, that we cannot share with the world? Let's build the project & distribute it on **npm**!

Install **Rollup** with

```bash
npm i -D rollup rollup-plugin-peer-deps-external rollup-plugin-postcss rollup-plugin-terser @rollup/plugin-babel @rollup/plugin-commonjs @rollup/plugin-node-resolve
```

Setup **Rollup Configuration**

```js
// /rollup.config.js
import resolve from "@rollup/plugin-node-resolve";
import { babel } from "@rollup/plugin-babel";
import external from "rollup-plugin-peer-deps-external";
import { terser } from "rollup-plugin-terser";
import postcss from "rollup-plugin-postcss";

export default {
  input: "src/index.js",
  output: [
    {
      file: "dist/index.js",
      format: "cjs",
    },
    {
      file: "dist/index.es.js",
      format: "es",
      exports: "named",
    },
  ],
  plugins: [
    postcss({
      plugins: [],
      minimize: true,
    }),
    babel({
      exclude: "node_modules/**",
      presets: ["@babel/env", "@babel/preset-react"],
      babelHelpers: "bundled",
    }),
    external(),
    resolve(),
    terser(),
  ],
  external: ["react", "react-dom"],
};
```

Add the script to build the files:

```json
// /package.json
{
  // ...
  "scripts": {
    // ...
    "build": "rollup -c"
  }
}
```

Now you can build out the project with

```bash
npm run build
```

Now you can publish the project on **npm**! Just make sure it has a **unique package name**.

## Wrapping Up

**Material UI** is a very mature library that has been around for years. [Olivier](https://www.linkedin.com/in/oliviertassinari/) definitely deserves a mention for creating such an outstanding library, used by even the humongous tech corporations!

If you want to create a library that truly competes with **Material UI**, you should be prepared to put in decades of grueling work first.

**Best of luck!**

![thumbs-up](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/meanihivveuayqbharoi.gif)

Finding **personal finance** too intimidating? Checkout my **Instagram** to become a [**Dollar Ninja**](https://www.instagram.com/the.dollar.ninja/)

# Thanks for reading

Need a **Top Rated Front-End Development Freelancer** to chop away your development woes? Contact me on [Upwork](https://www.upwork.com/o/profiles/users/~01c12e516ee1d35044/)

Want to see what I am working on? Check out my [Personal Website](https://tapajyoti-bose.vercel.app) and [GitHub](https://github.com/ruppysuppy)

Want to connect? Reach out to me on [LinkedIn](https://www.linkedin.com/in/tapajyoti-bose/)

I am a freelancer who will start off as a **Digital Nomad** in mid-2022. Want to catch the journey? Follow me on [Instagram](https://www.instagram.com/tapajyotib/)

Follow my blogs for **Weekly new Tidbits** on [Dev](https://dev.to/ruppysuppy)

**FAQ**

These are a few commonly asked questions I get. So, I hope this **FAQ** section solves your issues.

1. **I am a beginner, how should I learn Front-End Web Dev?**
   Look into the following articles:

    1. [Front End Development Roadmap](https://dev.to/ruppysuppy/front-end-developer-roadmap-zero-to-hero-4pkf)
    2. [Front End Project Ideas](https://dev.to/ruppysuppy/5-projects-to-master-front-end-development-57p)