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>
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
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
-
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
- 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
- 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"]
}
]
}
}
- Under the .eslintignore file paste the following -
build/
dist/
node_modules/
.snapshots/
*.min.js
*.css
*.svg
Step 3 - Some Cleanup
- First let us upgrade react and react dom to latest versions -
npm install react@latest react-dom@latest
- Update the types also -
npm install @types/react@latest @types/react-dom@latest --save-dev
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>;
}
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 />;
}
- 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"));
- 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
- Now run
npm run start
again in the browser you should see theExample 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
- We also need the following additional libraries -
npm install --save-dev @storybook/addon-a11y @storybook/addon-viewport storybook-readme
- 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",
},
};
- 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 />];
- 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>
- 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 />,
};
- Let us check our component story in storybook. In your terminal run -
npm run storybook
Step Six: Commitizen and Husky Setup
- In your terminal run -
npm install --save-dev git-cz commitizen
- Now run the following command -
npx commitizen init cz-conventional-changelog --save-dev --save-exact
- 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",
},
},
};
- Now install husky by running -
npm install husky --save-dev
- Activate husky hooks by running -
npx husky install
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
- Now add the
lint-staged
entry in package.json file -
"lint-staged": {
"*.+(ts|tsx)": [
"eslint --fix"
],
"*.+(json|css|md)": [
"prettier --write"
]
}
- Add the following scripts under the scripts section in package.json -
"commit": "git-cz",
"lint": "eslint . --color",
"lint:fix": "eslint . --fix",
"pretty": "prettier . --write",
- Let us test husky and commitlint first run the following in your terminal -
git add .
npm run commit
- The above
npm run commit
should open the git-cz questionnaire with the options and emojis mentioned under thechangelog.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)