Home

The Ultimate Guide to Setting Up Your React Component Library

2/9/2023

Image generated on Midjourney by Milton Boos JuniorImage generated on Midjourney by Milton Boos Junior

Have you ever had that components folder that kept getting copied from one project to another just to reuse good custom components? Or are you in a company with multiple frontends that have duplicate components? If you answered “yes” to either one of these questions, then it's time you create your own components library!

These libraries are almost indispensable in environments with a code base in constant evolution and with many components shared by several teams.

This article serves as a comprehensive guide for setting up your React component library from the ground up. You will learn how to create the library, showcase individual components, bundle using modern tools, and automatically publish it as a package for others to consume.

I'll demonstrate how by using the following tools:

1. React

Let's start by setting up the React component library using the TypeScript template.

npx create-react-app my-component-library --template typescript

Clean and organize your files the way you like, just remember that your main index.ts file should export your components instead of the default <App />. Make sure to change the name field in the package.json to the @git-user/my-component-library format.

For the sake of simplicity, let's create a React component that returns a <button/>. While you can create other components, just remember that React recommends that component libraries use the forwardRef hook to automatically pass a reference through a component to one of its children.

// ./src/components/Button/button.tsx
import { ReactNode, forwardRef, ComponentPropsWithoutRef } from "react";
 
interface ButtonProps extends ComponentPropsWithoutRef<"button"> {
  children: ReactNode;
}
 
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ children, ...props }, ref) => (
    <button {...props} ref={ref}>
      {children}
    </button>
  )
);
 
Button.displayName = "Button";
 
export { Button };
export type { ButtonProps };

Finally, export this component in the main index.ts.

// ./src/index.ts
export * from "./components/Button";

2. Storybook

For now, we can't see how our components look in isolation, so we'll also install Storybook to visualize them.

npx storybook init

Storybook will create default stories at /src/stories, but you can remove them as they are just examples of how you can take advantage of Storybook to showcase your components. Let's create a simple story for our Button!

// ./src/components/Button/button.stories.tsx
import { Button } from "./button";
 
export default {
  title: "Button",
  component: Button,
};
 
export const Default = () => <Button>Click me!</Button>;

Now, let's check it!

npm run storybook

3. Rollup

We have our tiny component library ready, but we still need to bundle everything. That's where Rollup comes to the rescue!

npm install --save-dev rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript

Configure it so that Rollup uses the main index.ts as the input, and bundle it to commonjs and esmodules. For TypeScript support, use the @rollup/plugin-typescript, but exclude unnecessary test and Storybook files.

// ./rollup.config.js
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
 
const config = {
  input: ["./src/index.ts"],
  output: [
    {
      file: "build/index.cjs.js",
      format: "cjs",
      sourcemap: true,
    },
    {
      file: "build/index.esm.js",
      format: "esm",
      sourcemap: true,
    },
  ],
  plugins: [
    resolve(),
    commonjs(),
    typescript({
      tsconfig: "./tsconfig.json",
      declaration: true,
      declarationDir: "build",
      exclude: ["**/*.test.*", "**/*.stories.*", "./src/test-utils/*"],
    }),
  ],
  external: ["react", "react-dom"],
};
 
export default config;

Change the package.json to use Rollup's build instead of react-scripts. Finally, change main and module to the respective commonjs and esmodules outputs. Make sure to specify the types declaration file in the types field.

"main": "build/index.cjs.js",
"module": "build/index.esm.js",
"types": "build/index.d.ts",
"scripts": {
  "build": "rollup --config",
},

4. Github

By now you should already have some commits in your GitHub repository, so let's take advantage of other GitHub tools to publish our package seamlessly.

First, let's add these fields to the package.json:

"repository": {
  "type": "git",
  "url": "git+https://github.com/juniorboos/my-component-library.git"
}

All you'll have to do next is create the release workflow using GitHub Actions! This workflow will be triggered with every release published on GitHub, installing dependencies, and building and publishing the library to the GitHub Package Registry. Don't forget to change node-version to the one you are currently using.

# .github/workflows/release.yml
name: Release
 
on:
  release:
    types: [published]
 
permissions:
  contents: read
  packages: write
 
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Setup node
        uses: actions/setup-node@v3
        with:
          node-version: 16
          registry-url: https://npm.pkg.github.com/
      - name: Install dependencies
        run: npm ci
      - name: Build
        run: npm run build
      - name: Publish
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

This is enough to automate the component library package release. Now you must go to your repo's releases and create the first one!

First, choose a tag. Then, pick a good title, write a description, and publish!!

This will automatically trigger a release with the new package's version. A reproduction of this can be found on my GitHub.

Conclusion

Setting up a React component library can be a great way to increase efficiency and consistency in your development process. By using tools such as Storybook, GitHub, and Rollup, you can create a centralized location for your components, document them, and easily share them with others.

Remember to keep your library up to date, document your components well, and test them frequently, in order to ensure that your library is meeting the needs of your team.