DEV Community

Arsalan Ahmed Yaldram
Arsalan Ahmed Yaldram

Posted on • Updated on

Setup a component library using react, typescript and create-react-library.

Introduction

This post is part of the Chakra UI clone series where we are cloning some chakra ui components using react, typeScript, styled-components and styled-system.

While building React components, I often look for tutorials, best methods, etc. searching the web learning new things trying out new libraries. I often love to read the code of various libraries, packages on github. I always find myself visiting - chakra-ui , react-rainbow, welcome-ui, radix-ui for inspiration and love reading their code on github (though I have not used these libraries beyond the basic stuff). A while back I cloned some chakra ui elements, using styled-components, styled-system and typescript and I would like to share my journey. You can check the repo, and try it out.

Chakra UI has this utility props for margin, padding, color, etc. that you can pass to components -

<Box bg="tomato" w="100%" p="20px" color="white">
  This is the Box
</Box>
Enter fullscreen mode Exit fullscreen mode

We will create our components with a similar API using styled-components and styled-system.

Update

I wrote this post back in August of 2021, if I were to start a react component library today, I would use modern build tools like Vite. In fact, I wrote another blog post on how to use Vite as our build tool you can check it here - https://dev.to/yaldram/react-component-library-setup-with-vite-typescript-styled-components-styled-system-and-storybook-55ad

Prerequisite

This series of posts is not recommended for a beginner, a good amount of familiarity with react, styled-components, styled-system, and typescript is expected. For an introduction to using the above together check my introductory post. In this tutorial we will setup up our repo -

  • Bootstrap our project using create-react-library, meaning we are creating a component library that can be used in other projects and even can be published to npm.
  • Setup eslint, upgrading packages.
  • Setup Storybook.
  • Setup Husky and git-cz for commits.

This tutorial covers a lot, but no worries all the code is available under the library-setup branch, check the repo.

Step One: Create a React Project with TypeScript

Let us start by bootstrapping our project using create-react-library. In your terminal run

npx create-react-library chakra-ui-clone
Enter fullscreen mode Exit fullscreen mode

After the above command executes, you go through a questionnaire, like package name, etc. Be sure to select typescript for template.
After the project has been bootstrapped you can see it comes pre-configured with eslint, prettier, react-testing-library, etc. Also the react version is not 17. We will change that, we will update eslint, use eslint airbnb style, update our react versions also we will setup storybook.

Step Two: Setup Eslint

  • First let us delete .eslintrc from the project and then in your terminal run -
npx eslint --init
Enter fullscreen mode Exit fullscreen mode
  • When running this command, you will need to answer some questions about the configuration:

    • How would you like to use ESLint?
    • Select: To check syntax, find problems, and enforce code style
    • What type of modules does your project use?
    • Select: JavaScript modules (import/export)
    • Which framework does your project use?
    • Select: React
    • Does your project use TypeScript?
    • Select: Yes
    • Where does your code run?
    • Select: Browser
    • How would you like to define a style for your project?
    • Select: Use a popular style guide
    • Which style guide do you want to follow?
    • Select: Airbnb: https://github.com/airbnb/javascript
    • What format do you want your config file to be in?
    • Select: JSON
    • After this, it will check the dependencies that need to be installed and then will ask:
    • Would you like to install them now with npm?
    • Select: Yes
  • Install the following addition devDependencies -

npm install --save-dev eslint-import-resolver-typescript
Enter fullscreen mode Exit fullscreen mode
  • Now our eslint package has been updated to the latest version and necessary dependencies are installed successfully. Let us remove the eslint config standard packages -
npm uninstall eslint-config-standard eslint-config-standard-react  eslint-plugin-standard
Enter fullscreen mode Exit fullscreen mode
  • Now under the root of your project create a new file called .eslintrc.json and paste the following -
{
  "env": {
    "node": true,
    "es6": true
  },
  "extends": [
    "plugin:react/recommended",
    "airbnb",
    "plugin:prettier/recommended",
    "prettier/standard",
    "prettier/react",
    "plugin:@typescript-eslint/eslint-recommended"
  ],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parser": "@typescript-eslint/parser",
  "settings": {
    "import/resolver": {
      "typescript": {}
    }
  },
  "parserOptions": {
    "ecmaFeatures": {
      "legacyDecorators": true,
      "jsx": true
    },
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "plugins": ["react", "react-hooks", "@typescript-eslint"],
  "rules": {
    "space-before-function-paren": 0,
    "react/prop-types": 0,
    "react/jsx-handler-names": 0,
    "react/jsx-fragments": 0,
    "react/no-unused-prop-types": 0,
    "import/export": 0,
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": "error",
    "react/jsx-props-no-spreading": "off",
    "import/prefer-default-export": 0,
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "max-len": ["warn", { "code": 80 }],
    "no-use-before-define": "off",
    "@typescript-eslint/no-use-before-define": ["error"],
    "react/jsx-filename-extension": ["warn", { "extensions": [".tsx"] }],
    "import/extensions": [
      "error",
      "ignorePackages",
      {
        "js": "never",
        "jsx": "never",
        "ts": "never",
        "tsx": "never"
      }
    ],
    "import/no-extraneous-dependencies": [
      "error",
      {
        "devDependencies": ["**/*.test.ts", "**/*.test.tsx", "**/*.stories.tsx"]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Under the .eslintignore file paste the following -
build/
dist/
node_modules/
.snapshots/
*.min.js
*.css
*.svg
Enter fullscreen mode Exit fullscreen mode

Step 3 - Some Cleanup

  • First let us upgrade react and react dom to latest versions -
npm install react@latest react-dom@latest
Enter fullscreen mode Exit fullscreen mode
  • Update the types also -
  npm install @types/react@latest @types/react-dom@latest --save-dev
Enter fullscreen mode Exit fullscreen mode
  • Also let us remove some unnecessary files from our project, first we remove styles.module.css and index.test.tsx from the src folder.

  • Next under the src/index.tsx file paste the following -

import * as React from "react";

export function ExampleComponent() {
  return <div>Example Component</div>;
}
Enter fullscreen mode Exit fullscreen mode
  • Now run npm run build, this will build our library. Whenever we create a new component or update existing ones, we must run npm run build so that we can import and run our components in the example directory. You can also run npm run start which will build your library each time you save a file, basically running build in watch mode.

  • From under the example/src folder, lets remove App.test.tsx, index.css, setupTests.ts.

  • Let us import our ExampleComponent in our example react project to test it and see if it is working as expected. Under the example/src/App.tsx paste the following code -

import * as React from "react";

import { ExampleComponent } from "chakra-ui-clone";

export function App() {
  return <ExampleComponent />;
}
Enter fullscreen mode Exit fullscreen mode
  • And under example/src/index.tsx paste the following code -
import * as React from "react";
import ReactDOM from "react-dom";

import { App } from "./App";

ReactDOM.render(<App />, document.getElementById("root"));
Enter fullscreen mode Exit fullscreen mode
  • Now cd into the example folder and run npm run start, it will give some wired babel version mismatch error so under the example root folder (not example/src) create a .env file in it paste -
SKIP_PREFLIGHT_CHECK=true
Enter fullscreen mode Exit fullscreen mode
  • Now run npm run start again in the browser you should see the Example Component text in your browser.

Step Four - Storybook Setup

  • For storybook we will use the latest pre-released csf format. In your main directory i.e. chakra-ui-clone directory run -
npx -p @storybook/cli sb init --story-format=csf-ts
Enter fullscreen mode Exit fullscreen mode
  • We also need the following additional libraries -
npm install --save-dev @storybook/addon-a11y @storybook/addon-viewport storybook-readme
Enter fullscreen mode Exit fullscreen mode
  • Now we have a stories folder under the src folder, delete the stories folder.
  • Under the root we also have .storybook folder with main.js & preview.js files.
  • Under the main.js paste the following code -
module.exports = {
  features: {
    previewCsfV3: true,
  },
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: [
    "storybook-readme/register",
    "@storybook/addon-links",
    "@storybook/addon-essentials",
    "@storybook/addon-knobs",
    "@storybook/addon-a11y",
  ],
  typescript: {
    reactDocgen: "react-docgen-typescript",
  },
};
Enter fullscreen mode Exit fullscreen mode
  • Under the preview.js file paste the following code -
import React from "react";
import { create } from "@storybook/theming";
import { addReadme } from "storybook-readme";

const basicTheme = create({
  base: "light",
  brandTitle: "Chakra UI Clone",
});

export const parameters = {
  options: {
    showPanel: true,
    panelPosition: "bottom",
    theme: basicTheme,
    storySort: {
      order: ["Atoms", "Molecules", "Organisms"],
      method: "alphabetical",
    },
  },
  readMe: {
    codeTheme: "duotone-sea",

    StoryPreview: ({ children }) => <div>{children}</div>,

    HeaderPreview: ({ children }) => <div>{children}</div>,

    FooterPreview: ({ children }) => <div>{children}</div>,

    DocPreview: ({ children }) => (
      <div style={{ padding: "1.6rem" }}>{children}</div>
    ),
  },
  actions: { argTypesRegex: "^on[A-Z].*" },
  knobs: {
    escapeHTML: false,
  },
  backgrounds: {
    default: "white",
    values: [
      { name: "white", value: "#fff" },
      { name: "dark", value: "#000" },
    ],
  },
};

export const decorators = [addReadme, (Story) => <Story />];
Enter fullscreen mode Exit fullscreen mode
  • Create one more file under the .storybook folder by the name preview-body.html for loading our fonts and add -
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
  href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;900&display=swap"
  rel="stylesheet"
/>

<style>
  * {
    padding: 0;
    margin: 0;
  }

  body {
    font-family: Montserrat, sans-serif;
  }

  /*
    Styling issue for Docs -> Show Code
    resolved with the following styles
  */
  code[class*="language-"],
  pre[class*="language-"] {
    color: white;
    text-shadow: none;
  }
</style>
Enter fullscreen mode Exit fullscreen mode
  • Let us create a story, under the src folder, create a new file index.stories.tsx and add -
import * as React from "react";

import { ExampleComponent } from ".";

export default {
  title: "ExampleComponent",
};

export const Default = {
  render: () => <ExampleComponent />,
};
Enter fullscreen mode Exit fullscreen mode
  • Let us check our component story in storybook. In your terminal run -
npm run storybook
Enter fullscreen mode Exit fullscreen mode

Step Six: Commitizen and Husky Setup

  • In your terminal run -
npm install --save-dev git-cz commitizen
Enter fullscreen mode Exit fullscreen mode
  • Now run the following command -
 npx commitizen init cz-conventional-changelog --save-dev --save-exact
Enter fullscreen mode Exit fullscreen mode
  • Under the root of the project create a file by the name changelog.config.js in it paste the following code -
/* eslint-disable prettier/prettier */
module.exports = {
  disableEmoji: false,
  list: [
    "test",
    "feat",
    "fix",
    "chore",
    "docs",
    "refactor",
    "style",
    "ci",
    "perf",
  ],
  maxMessageLength: 64,
  minMessageLength: 3,
  questions: [
    "type",
    "scope",
    "subject",
    "body",
    "breaking",
    "issues",
    "lerna",
  ],
  scopes: [],
  types: {
    chore: {
      description: "Build process or auxiliary tool changes",
      emoji: "πŸ€–",
      value: "chore",
    },
    ci: {
      description: "CI related changes",
      emoji: "🎑",
      value: "ci",
    },
    docs: {
      description: "Documentation only changes",
      emoji: "✏️",
      value: "docs",
    },
    feat: {
      description: "A new feature",
      emoji: "🎸",
      value: "feat",
    },
    fix: {
      description: "A bug fix",
      emoji: "πŸ›",
      value: "fix",
    },
    perf: {
      description: "A code change that improves performance",
      emoji: "⚑️",
      value: "perf",
    },
    refactor: {
      description: "A code change that neither fixes a bug or adds a feature",
      emoji: "πŸ’‘",
      value: "refactor",
    },
    release: {
      description: "Create a release commit",
      emoji: "🏹",
      value: "release",
    },
    style: {
      description: "Markup, white-space, formatting, missing semi-colons...",
      emoji: "πŸ’„",
      value: "style",
    },
    test: {
      description: "Adding missing tests",
      emoji: "πŸ’",
      value: "test",
    },
  },
};
Enter fullscreen mode Exit fullscreen mode
  • Now install husky by running -
npm install husky --save-dev
Enter fullscreen mode Exit fullscreen mode
  • Activate husky hooks by running -
npx husky install
Enter fullscreen mode Exit fullscreen mode
  • Adding hooks to husky we do have a command but it did not work for me so what I did was to create a file called pre-commit under the .husky folder.

  • Under pre-commit paste the following code -

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
Enter fullscreen mode Exit fullscreen mode
  • Now add the lint-staged entry in package.json file -
"lint-staged": {
    "*.+(ts|tsx)": [
      "eslint --fix"
    ],
    "*.+(json|css|md)": [
      "prettier --write"
    ]
  }
Enter fullscreen mode Exit fullscreen mode
  • Add the following scripts under the scripts section in package.json -
"commit": "git-cz",
"lint": "eslint . --color",
"lint:fix": "eslint . --fix",
"pretty": "prettier . --write",
Enter fullscreen mode Exit fullscreen mode
  • Let us test husky and commitlint first run the following in your terminal -
  git add .
  npm run commit
Enter fullscreen mode Exit fullscreen mode
  • The above npm run commit should open the git-cz questionnaire with the options and emojis mentioned under the changelog.config.js. Fill in the questions, after that is done husky should kick in and run the lint and pretty scripts mentioned under lint-staged in package.json

Summary

There you go our component library setup is done. Please check the library-setup for all the code that we covered here repo. In the next tutorial we will setup the theme values, until next time PEACE.

Top comments (0)