Develop and Publish a React component library using “Create React App typescript”

Alex Disdier
The Startup
Published in
8 min readMay 6, 2020
Photo by Antonio Molinari on Unsplash

While working for my current employer, I was introduced to their UI component library built in javascript using react. Its purpose is to keep our main product components small, function-specific and DRY.

The library possesses UI reusable components, following best practices in terms of accessibility while respecting order content in HTML for screen readers.

My task was to build a form builder library in TypeScript respecting the same guidelines: small, reusable with accessible components.

I decided to use Create React App Typescript — (CRA TS) as a boilerplate and convert it from an app to a library. This head start gives us common features built-in like Babel, Webpack, Jest, and a hot reloading development server.

During this challenging journey, I wanted to document my process so I could refer to it if needed.

Why not share those findings with other people who wish to create their own library. So here we are…

Content:

  • boilerplate cra typescript
  • style and theming
  • storybook to display our library
  • basic eslint rules
  • create and test a button component
  • converting CRA into a Library
  • publish on NPM
  • semantic release

This tutorial does not go into the whys and wherefores. It solely focuses on implementation.

Create React App

The fastest way to get started using TypeScript with React is using Facebook’s own Create React App (CRA) one line command.

yarn create react-app your-library-name --template typescript

Since we’re building a library and not an app, we can safely remove several unnecessary files.

cd your-library-name/src && 
rm App.tsx App.css App.test.tsx index.tsx index.css logo.svg serviceWorker.ts && cd .. && rm -rf public

Next, we install our library dependencies. For the purpose of this tutorial, I use yarn. When you see a -D in the command line, it means installing that dependency for development only, the module will not be shipped with our final library.

When you see the following terminal command, it will open the file in vscode.

code filename

You can use instead this command which will open the file in your default IDE (Integrated Development Environment).

open filename

Style and theming

Let’s install react-jss to style our components and bring dynamic theming.

yarn add react-jss #v.10.1.1 as of May 5th 2020

We need to create a base css file and a theme stylesheet which we will then provide to our ui components.

mkdir src/style
touch src/style/base.css src/style/globalStyle.ts
code src/style/base.css

If you don’t have your preferred base css file, you can copy the following.

base.css

If you don’t have a theme stylesheet, here is a quick a sample:

open the globalTheme file.

code src/style/globalStyle.ts

and paste the following:

export default {
colorPrimary: '#3e9f92',
colorSecondary: '#8892b0',
};

Now that we have setup our style manager, let’s install storybook and its relevant add-on packages. We’ll then setup a basic config file to get you started.

Storybook

Enables us to display our library, document and interact with its components. (About storybook).

Since we’re using CRA, the automatic setup from storybook installs all the necessary packages. (Check out the latest of Config for storybook).

# check you are in the correct path ./your-library-name
pwd
npx -p @storybook/cli sb init

Knobs (more details):

yarn add -D @storybook/addon-knobs storybook-readme

In order for typescript to be able to import README files, you need to create a modules’ file and declare the module extension .md

create the following file

touch src/modules.d.tscode src/modules.d.ts

and paste the following

declare module '*.md';

Then you need to navigate to ./storybook/main.js

code .storybook/main.js

and change the stories extension from js to tsx. We’ll also add the addons

module.exports = {
stories: ['../src/**/*.stories.tsx'],
addons: [
'@storybook/preset-create-react-app',
'@storybook/addon-actions',
'@storybook/addon-links',
'@storybook/addon-knobs',
'storybook-readme'
],
};

Remove the ./src/stories as we’ll make our own stories.

rm -rf ./src/stories

Create a welcome story inside the .storybook folder

touch .storybook/welcome.stories.tsxcode .storybook/welcome.stories.tsx

And paste the following:

import React from "react";
import { storiesOf } from "@storybook/react";
const stories = storiesOf("Welcome", module);stories.add("to your library", () => (
<div>This is your new library. </div>
));

We need to setup the storybook configuration file in order to provide our stylesheet theme to the components, the README addon and manage the way we load our stories.

touch .storybook/config.js
code .storybook/config.js

Here is a gist of a storybook configuration sample.

Storybook configuration file (version 5.3.14)

Let’s simplify storybook starting script command in the package.json.

code package.json"scripts": {
"start": "start-storybook -p 9009 -s public",
// remove the following scripts
// "storybook": "start-storybook -p 9009",

Linting

In order to automate linting, we can install the following packages.

yarn add -D eslint-config-airbnb eslint-config-prettier eslint-plugin-prettier prettier

Then we add some linting scripts to our package.json

"scripts": {
...
"lint": "eslint './src/**/*.{js,jsx,ts,tsx}'",
"lint:fix": "eslint --fix './src/**/*.{js,jsx,ts,tsx}'",
...
},

We now create an eslint file which will extend the one from create-react-app.

touch .eslintrc.js code .eslintrc.js

If you don’t have one of your liking, you can kindly copy this one I setup for myself.

.eslintrc.js

You also need to set up your IDE eslint configuration which can give you a productivity boost (e.g: fixing lint on save).

If you are using vscode and you don’t have one of your liking, you can copy mine.

Pull up the settings by keying

cmd + shift + p (it brings the palette search bar)

and select

preferences: Open Settings (JSON)

./settings

Create and test our button component

Let’s create some folders and files serving as a base for our library. We create only one atom for now: a button. (further reading on atomic design)

We use @testing-library/react which already comes with CRA. I won’t go into any details, you can check out their documentation if you wish to dive further into unit/integration testing.

Let’s create the setup directory

# check you are in the correct path ./your-library-name
pwd
# where we export all our components
touch src/index.ts
# library entry point
code src/index.ts

copy and paste the following line

# exporting all components
export * from './components';

Then we create the rest of the directory

mkdir src/components# where we export all our atoms
touch src/components/index.ts
# components entry point
code src/components/index.ts

copy and paste the following line

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

Let’s keep going

mkdir src/components/buttoncd src/components/button
touch index.tsx button.style.ts button.test.tsx button.stories.tsx README.md

You can copy and paste these boilerplate files.

./button/index.tsx
./button/button.style.ts
./button/button.stories.tsx
./button/button.test.tsx

copy the raw text from this README file.

./button/README.md

You should now be able to test the button component.

cd ../../../# check you are in the correct path ./your-library-name
pwd
# test script from package.json
yarn test
# ctrl + c to quit watching your test

Try running the lint command to check it enforces the eslint config.

yarn lint

You should see at least one error …expected exception block…

Let’s fix this lint problem by simply running our script setup from earlier within package.json

yarn lint --fix

In the console, you shouldn’t see anymore errors. Only the message Done in …s should appear which means it successfully corrected the problem.

You can now start the storybook to display the component.

# start storybook script from package.json
yarn start
# ctrl + c to quit storybook (aka killing the process)

Let’s wrap up our library by enabling the build and publishing to npm.

ps: if you don’t have an account on npm, I encourage you to signup before carrying on this tutorial.

CRA into Library

We now need to edit our package.json which holds the configuration of our library.

We’ll give it an entry point, the name of the built library folder, a github repository (even though it is not required to publish to npm, I strongly urge you to use a version control software) and finally some extra scripts.

First, add one extra package which can copy extra file needed by the library such as its base.css.

yarn add -D copyfilescode package.json
  ...
"license": "MIT",
"main": "lib/src/index.js",
"private": false // unless you're publishing private npm packages
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"repository": {
"type": "git",
"url": "https://github.com/you-the-author/your-library.git"
},
...
"scripts": {
...
"clean": "rm -rf ./lib",
"prebuild": "yarn clean",
"build": "tsc --build \"./tsconfig.json\"",
"copy-files": "copyfiles -u 1 src/style/base.css lib/",
"postbuild": "yarn run copy-files",
...
},

Before running our build, we need to edit our tsconfig.json. You can copy my settings if you don’t have one.

./tsconfig.json

Now if you run

yarn build

you will see your lib folder freshly created at the root directory and that means you’re ready to publish.

…oops. don’t forget to add /lib to your .gitignore so your library is not pushed onto your github repo.

Publishing your library to npm

This should technically be the easiest part. You should first create you npm account.

  1. Sign in
npm login

You’ll be prompted to enter your username, password and email address.

Since you already have a package.json file,

2. You can optionally add some more details to it before publishing such as:

  "name": "your-library-name",
"version": "0.1.1",
"description": "A React component library",
"author": "You",
"license": "MIT",
"keywords": [
"react",
"javascript",
"typescript"
],
...

3. You can publish your package

npm publish

If you run into an error, the package name has already been taken on npm, you should proceed to a simple search on npm’s website to make sure your name does not exist yet. Then try running the publish command again with a new name.

BONUS — Semantic Release and deploy static site to github

Semantic Release will make publishing a breeze. You won’t need to manually update your library version anymore.

In order to automate the publishing process, you need a module called semantic-release.

I recommend to install their interactive CLI. (Command Line Interface). Just run the following command in your project folder and follow the instructions:

semantic-release-cli setup

Every time you merge into master, it will automatically publish to npm.

Finally, you can also deploy your static storybook to Github pages

Conclusion

I hope this tutorial was useful. Don’t hesitate to reach out if you need more details or if you’re encountering some bugs (you got to love those, they just make our lives more exciting).

On a separate note, if you wish to kickstart your project without going through this tutorial, you can use the CLI (Command Line Interface) I use for my setup.

npx @alexdisdier/create lib-ts you-library-name

If you’re curious, you can check out the library I’m building. Hope you’ll indulge me as this is in working progress.

--

--