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!
Initializing Project
Initialize a new project with
npm init
Add the dependencies using:
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
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:
module.exports = {
// ...
stories: [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
],
};
You can now start up the storybook project with
npm run storybook
To add CSS Modules support to the project, install the following:
npm i -D @storybook/addon-postcss storybook-css-modules-preset
Update the /.storybook/main.js
configuration with:
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.
/* /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;
}
// /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;
// /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.
// /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.
// /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
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
// /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:
// /package.json
{
// ...
"scripts": {
// ...
"build": "rollup -c"
}
}
Now you can build out the project with
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 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!
Finding personal finance too intimidating? Checkout my Instagram to become a Dollar Ninja
Thanks for reading
Need a Top Rated Front-End Development Freelancer to chop away your development woes? Contact me on Upwork
Want to see what I am working on? Check out my Personal Website and GitHub
Want to connect? Reach out to me on LinkedIn
I am a freelancer who will start off as a Digital Nomad in mid-2022. Want to catch the journey? Follow me on Instagram
Follow my blogs for Weekly new Tidbits on Dev
FAQ
These are a few commonly asked questions I get. So, I hope this FAQ section solves your issues.
-
I am a beginner, how should I learn Front-End Web Dev?
Look into the following articles:
Top comments (3)
how to solve images & svgs used by each components?
I have tried some methods.but nothing done.
Typically you shouldn't use rastor images in a ui library. To use vector images like
svg
s, create components for them (sincesvg
elements are valid html & jsx elements)there is dedicated plugin on rollup for svg ang other images, you probably need to check
@rollup/plugin-image
androllup-plugin-svg-import
packages.Though there is a caveat, especially for images, they will be converted into base64 so your library will be larger