React Component Library With Vite - JS Template

React Component Library With Vite - JS Template

  1. Intro

  2. What is a Design System?

  3. Building a basic Design System Library using React

  4. Button as a Design System Component

  5. Component Library With Microbundle

  6. Component Library With Vite - JS Template

Vite

Vite is a modern build tool that provides easier setup and faster build time for modern web projects. It supports Javascript and Typescript frameworks like React, Preact, Vue, svelte, lit, and Vanilla.

Read more: https://vitejs.dev/guide/why.html

For this article, to create a library of React we need the support of Vite. It can be done with just a few things:-

  1. Vite Setup

  2. Vite Config

  3. Add your code

  4. Build your code

  5. and It's done

Step 1: Vite Setup

We will setup a React-JS project with vite.

Run the command below and follow the instructions

npm create vite@latest react

Enter the project name.

Select framework

Select Language

cd <project folder> and Install dependencies npm i

And, it's done

Folder Structure

Step 2: Add React Components code

Add a new folder to src folder - components

Add the code for Button component -the component code, styling and create a common index.js file that will import the components and export them.

src/components/Button/index.jsx - Note .jsx here.

import PropTypes from 'prop-types'; // ES6
import StyledButton, { StyledSpanContainerForIcon } from './Button.style';

function Button({ children, ...props }) {
    return <StyledButton {...props}>{children}</StyledButton>;
}

export const IconButton = ({ text, icon, left = false, ...props }) => {
    return (
        <StyledButton left={left} {...props}>
            {text && <StyledSpanContainerForIcon left={left}>{text}</StyledSpanContainerForIcon>}
            {typeof icon === 'function' ? icon() : icon}
        </StyledButton>
    );
};

export default Button;

Button.propTypes = {
    children: PropTypes.element
}

IconButton.propTypes = {
    text: PropTypes.string,
    icon: [PropTypes.func, PropTypes.element],
    left: PropTypes.bool
}

src/components/Button/Button.style.js

import tw, { styled } from 'twin.macro';

const defaultButtonStyle = tw`
    flex
    justify-center
    items-center

    rounded
    text-sm
    px-5
    py-2
    cursor-pointer
    border-solid
`;

const primaryStyle = tw`
    text-white
    bg-primary
    border-none

    hover:shadow-base
    hover:border-border-base

    active:bg-primary-active
    active:drop-shadow-none

    focus:bg-primary-active
    focus:drop-shadow-none

    disabled:opacity-65
    disabled:cursor-not-allowed
`;

const secondaryStyle = tw`
    bg-white
    border
    border-red-shade
    text-red-shade 

    hover:bg-secondary
    hover:shadow-base

    active:bg-secondary
    active:shadow-base

    focus:bg-secondary
    focus:shadow-base

    disabled:opacity-65
    disabled:drop-shadow-none
    disabled:bg-white
    disabled:cursor-not-allowed
`;

const tertiaryStyle = tw`
    bg-white
    border
    border-tertiary
    text-text-tertiary

    hover:bg-secondary
    hover:shadow-base

    active:bg-secondary
    active:shadow-base

    focus:bg-secondary
    focus:shadow-base

    disabled:opacity-65
    disabled:drop-shadow-none
    disabled:bg-white
    disabled:cursor-not-allowed
`;

const quarternaryStyle = tw`
    bg-white
    border-none
    text-text-tertiary

    hover:bg-quarternary

    active:bg-quarternary

    focus:bg-quarternary

    disabled:bg-quarternary
    disabled:opacity-65
    disabled:cursor-not-allowed
`;

const ButtonType = ({ primary, secondary, tertiary, quarternary }) => [
    defaultButtonStyle,
    primary && primaryStyle,
    secondary && secondaryStyle,
    tertiary && tertiaryStyle,
    quarternary && quarternaryStyle,
    !primary && !secondary && !tertiary && !quarternary && primaryStyle, // default primary
];

// Icon Button Styles
const iconRowReverse = tw`
    flex-row-reverse
`;
const IconButton = ({ left = false }) => [left && iconRowReverse];

// Append Icon Button style to the same styled button
const StyledButton = styled.button(() => [ButtonType, IconButton]);
export default StyledButton;

// Manage margin between button text and icon
const iconStyleLeft = tw`
    ml-1
`;
const iconStyleRight = tw`
    mr-1
`;
const IconAlignment = ({ left = false }) => [left ? iconStyleLeft : iconStyleRight];
export const StyledSpanContainerForIcon = styled.span(() => [IconAlignment]);

src/component/index.js

export { default as Button } from './Button';
export { IconButton } from './Button';

tailwind.config.js

// eslint-disable-next-line no-undef
module.exports = {
    content: ['./src/**/*.{js,jsx,ts,tsx}'],
    theme: {
        extend: {
            colors: {
                'red-shade': '#eb575c',
                'primary': '#EA575C',
                'primary-active': '#E62930',
                'border-base': '#adadad',
                'secondary': '#F5F5F5',
                'tertiary': '#DDDDDD',
                'text-tertiary': '#333333',
                'quarternary': '#D9D9D9',
            },
        },
        boxShadow: {
            base: '0 3px 6px rgb(0 0 0 / 16%), 0 3px 6px rgb(0 0 0 / 23%)'
        },
        opacity: {
            65: '.65',
        }
    },
    plugins: [],
};

package.json

{
  "name": "react-vite-lib",
  "private": true,
  "version": "0.0.5",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/react-vite-library.es.js",
      "require": "./dist/react-vite-library.umd.js"
    }
  },
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "peerDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "files": [
    "dist"
  ],
  "dependencies": {
    "@emotion/react": "^11.10.8",
    "@emotion/styled": "^11.10.8",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "tailwindcss": "^3.3.2",
    "twin.macro": "^3.3.1",
    "vite-plugin-babel-macros": "^1.0.6"
  },
  "devDependencies": {
    "@types/react": "^18.0.28",
    "@types/react-dom": "^18.0.11",
    "@vitejs/plugin-react": "^4.0.0",
    "eslint": "^8.38.0",
    "eslint-plugin-react": "^7.32.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.3.4",
    "vite": "^4.3.2"
  }
}

don't forget to install dependencies like emotions, tailwind, twi-macro as we are using them to build our component libraries.

Runt the command npm i.

Note: export, files, peerDependencies in the package.json. These are required to build and use the build package in the app when we install the library package.

Step 3: Build the Component Library

vite.config.js - Let's setup vite config that can be used to build the react-js code to make a component library. Copy & paste this vite config and install the new dependencies used in the config.

install vite-plugin-babel-macros for babel macros

import { resolve } from 'node:path'
import macrosPlugin from 'vite-plugin-babel-macros';

import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

import * as packageJson from './package.json'
// https://vitejs.dev/config/

export default defineConfig(() => ({
  plugins: [
    react(),
    macrosPlugin()
  ],
  define: {
    'process.env': {},
  },
  build: {
    lib: {
      entry: resolve('src', 'component/index.js'),
      name: 'ReactViteLibrary',
      formats: ['es', 'cjs', 'umd'],
      fileName: (format) => `react-vite-library.${format}.js`,
    },
    manifest: true,
    rollupOptions: {
      external: [...Object.keys(packageJson.peerDependencies)],
    },
  }
}))

Now, Let's build our component library

Build the Library

npm run build

Make a package

npm pack

Note: Make sure to update the version in the package.json after every new build

To use the library in an app

Go to the app and install the library package as in the below-mentioned format

npm add <package-path>/<package-version.tgz

command

npm add ../react-vite-lib/react-vite-lib-0.0.5.tgz

and It's done.

Your library is built and ready to use.


Check the complete code here on GitHub

If you have reached here, I would appreciate the time and effort you put into going through this article. I hope it is worth your time. Please leave honest feedback and let me know how can I improve this project.

I will keep adding more components to this repo soon. The planned components are InputBox, DateRangePicker, Toggle, Dropdwn, MultiBox, ComboBox, and Form. But the style would be as per my current projects requirement only, you can fork the repo and change it as per your need. I will also add proper documentation, storybook integration and jest along with the code.


  1. Intro

  2. What is a Design System?

  3. Building a basic Design System Library using React

  4. Button as a Design System Component

  5. Component Library With Microbundle

  6. Component Library With Vite - JS Template

I hope you are enjoying this series and learning something useful. Please help me improve my work by providing proper feedback via adding comments or do send me an email.