How to create and publish React Typescript npm package with demo and automated build
Tags: react, typescript, npm, github
Published: 2022-06-30 09:12:27

In this article I will show you how to create React module on Typescript, create demo page, description for it and publish as node package. We will use Eslint, Jest, Prettier, github pages for demo-page and github-actions to setup automated builds in releases.

---

**Introduction**

Why did I decide to write one more article about publishing npm-packages? When I needed to create such a package by myself, I found that most of the instructions on the Internet were either outdated or very short and useless in production.

I will try to describe all the steps as clear as I can, so you can create your own project from the scratch. Let’s get started!

I propose to create a simple react application, which will consist of two buttons that increase or decrease the value of the counter.

This is how our application will look like:

Demo: [https://gapon2401.github.io/my-react-typescript-package](https://gapon2401.github.io/my-react-typescript-package)

Repo: [https://github.com/gapon2401/my-react-typescript-package](https://github.com/gapon2401/my-react-typescript-package)

---
We have 14 steps to deal with everything:

1. [Preparing the project](#step-1-preparing-the-project)
2. [Create your react application](#step-2-create-your-react-application)
3. [Configure git](#step-3-configure-git)
4. [Configure ESLint](#step-4-configure-eslint)
5. [Configure Prettier](#step-5-configure-prettier)
6. [Adding tests with Jest](#step-6-adding-tests-with-jest)
7. [Configure package.json and prepare for publishing](#step-7-configure-packagejson-and-prepare-for-publishing)
8. [Commit and push your code](#step-8-commit-and-push-your-code)
9. [Publishing to NPM](#step-9-publishing-to-npm)
10. [Creating of the example folder](#step-10-creating-of-the-example-folder)
11. [Setup automated builds](#step-11-setup-automated-builds)
12. [Demo page](#step-12-demo-page)
13. [README.md file and shields.io](#step-13-readmemd-file-and-shieldsio)
14. [Making release](#step-14-making-release)

---

### Step 1. Preparing the project

- In command line of your project execute:

```bash
npm init -y
```

It will create `package.json` file with default values, we will change it a bit later.

- Create folder `src`

Here we will keep all project files.

- Add React and Typescript to the project (I’m going to use `yarn`, if you don’t mind)

```bash
yarn add -D react react-dom typescript @types/react
```

Now we have `node_modules` folder and a `yarn.lock` — not bad!

- In order to compile typescript, create a `tsconfig.json` file in the project root with the contents

```json
{
  "include": ["src"],
  "exclude": [
    "dist",
    "node_modules"
  ],
  "compilerOptions": {
    "module": "esnext",
    "lib": ["dom", "esnext"],
    "importHelpers": true,
    "declaration": true,
    "sourceMap": true,
    "rootDir": "./src",
    "outDir": "./dist/esm",
    "strict": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "moduleResolution": "node",
    "jsx": "react",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
  }
}
```

You can take a look at all possible properties here: [https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig)

From the main:

-- `rootDir` — the root folder within your source files,

-- `include` — specifies the files to be included in the TypeScript project,

-- `exclude` — specifies the files to be excluded in the TypeScript project. We don’t want to include dist folder and all node_modules ,

-- `outDir` — this is the location for the compiled output

- Create empty `README.md` and `LICENSE` files.

It’s up to you what license to use. I will use MIT, you can check the contents of that file.

Take a look at the structure we should have:

![Project structure after step 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bhbvunplntmr87ad6cso.jpg)

---

### Step 2. Create your react application

Inside `src` create the folder `components`. There you can keep all the components of your project. We will create here within this article only one file `App.tsx` with the contents:

```js
import React, { useState } from 'react'

type Props = {
    value?: number
}
const MyCounter = ({ value = 0 }: Props) => {
    const [counter, setCounter] = useState(value);

    const onMinus = () => {
        setCounter((prev) => prev - 1)
    };

    const onPlus = () => {
        setCounter((prev) => prev + 1)
    };

    return (
        <div>
            <h1>Counter: {counter}</h1>
            <button onClick={onMinus}>-</button>
            <button onClick={onPlus}>+</button>
        </div>
    )
}

export default MyCounter
```

This is our React application.

Now create file `index.ts` inside `src` folder. Export our module.

```js
import MyCounter from './components/App'

export { MyCounter }
```

It’s time to compile the project! 🤗

Let’s change our `package.json` and replace `scripts` section:

```json
{
  "name": "my-react-typescript-package",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc"
  },
  "keywords": [],
  "author": "gapon2401",
  "license": "ISC",
  "devDependencies": {
    "@types/react": "^18.0.12",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "typescript": "^4.7.3"
  }
}
```

Run in terminal:

```bash
yarn build
```

Folder `dist` should be appeared with all compiled code.

Congratulations! 🎉 We have created the application, which is compiled, a few more steps and we’re done! (no..)

Compare your project structure after step 2:

![Project structure after step 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8bza30aj4y0uejk8szgc.jpg)

---

### Step 3. Configure git

We quickly have created the project, now it’s time for the git.

Initialize `git` in the root of the project

```bash
git init
```

Create file `.gitignore` in project root with contents:

```bash
node_modules
.idea
dist
yarn-error.log
```

I added `.idea`, because I’m developing with the help of Jetbrains IDE.

In `.gitignore` we list those files, directories that we would like not to include in the repository.

Create git repo on [github](https://github.com/). Later we will create initial commit and connect remote git with our local git.

---

### Step 4. Configure ESLint

ESLint statically analyzes your code to quickly find problems. We need it only in development.

Add eslint and all necessary components:

```bash
yarn add -D eslint eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser
```

You can configure it as you want, there is a big user guide: [https://eslint.org/docs/user-guide/configuring/](https://eslint.org/docs/user-guide/configuring/)

I suggest you create an `.eslintrc` file at the root of the project:

```json
{
  "root": true,
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "@typescript-eslint",
    "react",
    "react-hooks"
  ],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "@typescript-eslint/no-non-null-assertion": "off",
    "@typescript-eslint/ban-ts-comment": "off",
    "@typescript-eslint/no-explicit-any": "off"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "env": {
    "browser": true,
    "node": true
  },
  "globals": {
    "JSX": true
  }
}
```

Add `.eslintignore`:

```bash
node_modules
dist
```

We will ignore checking `dist` folder with compiled files and `node_modules`.

Change `package.json`, add to `scripts` section:

```bash
"lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\""
```

Here is my `package.json`:

```json
{
  "name": "my-react-typescript-package",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc",
    "lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\""
  },
  "keywords": [],
  "author": "gapon2401",
  "license": "ISC",
  "devDependencies": {
    "@types/react": "^18.0.12",
    "@typescript-eslint/eslint-plugin": "^5.27.1",
    "@typescript-eslint/parser": "^5.27.1",
    "eslint": "^8.17.0",
    "eslint-plugin-react": "^7.30.0",
    "eslint-plugin-react-hooks": "^4.5.0",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "typescript": "^4.7.3"
  }
}
```

Now, run:

```bash
yarn lint
```

You shouldn’t have any mistakes.

Compare your project structure after step 4:

![Project structure after step 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ponunviuhj5bhuodyia6.jpg)

---

### Step 5. Configure Prettier

Prettier is a code formatter. It is convenient to use it when you work in a team so that everyone’s code meets the same standard.

In terminal run the command:

```bash
yarn add -D eslint-config-prettier eslint-plugin-prettier prettier
```

In project root create `.prettierrc.json`:

```json
{
  "bracketSpacing": true,
  "singleQuote": true,
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": false,
  "printWidth": 120,
  "jsxSingleQuote": true,
  "endOfLine": "auto"
}
```

Read more about all options: [https://prettier.io/docs/en/options.html](https://prettier.io/docs/en/options.html).

Add prettier plugin to `.eslintrc`:

```json
{
  "root": true,
  "extends": [
    "prettier",
    "plugin:prettier/recommended",
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "@typescript-eslint",
    "prettier",
    "react",
    "react-hooks"
  ],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "@typescript-eslint/no-non-null-assertion": "off",
    "@typescript-eslint/ban-ts-comment": "off",
    "@typescript-eslint/no-explicit-any": "off"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "env": {
    "browser": true,
    "node": true
  },
  "globals": {
    "JSX": true
  }
}
```

We have finished the setup of prettier, now try to run `yarn lint` in order to check formatting errors. If you don’t have any, so you didn’t pass the human test 😆

![Eslint prettier errors](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z08y7gf4h6zd5t6nli85.jpg)

It’s time to fix them! Add to `scripts` in `package.json`:

```bash
"prettier": "prettier --write \"{src,tests,example/src}/**/*.{js,ts,jsx,tsx}\""
```

Ok, run `yarn prettier` and you will get something similar to:

![yarn prettier](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rkag8hipv8tahjn0yz6o.jpg)

Now after `yarn lint` you should not have any errors. Check the files `src/components/App.tsx` and `src/index.ts`, they have changed.

My `scripts` section in `package.json`:

```bash
"scripts": {
  "build": "tsc",
  "lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\"",
  "prettier": "prettier --write \"{src,tests,example/src}/**/*.{js,ts,jsx,tsx}\""
}
```

Compare your project structure after step 5:

![Project structure after step 5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6r8wwadesuzfrd4bs9cl.jpg)

---

### Step 6. Adding tests with Jest

For creating tests I’m using [Jest](https://jestjs.io/) library. It’s pretty useful and powerful testing framework. How difficult the tests will be, of course, is up to you.

Add `jest` to your project:

```bash
yarn add -D jest jest-canvas-mock jest-environment-jsdom ts-jest @types/jest @testing-library/react
```

Create at the project root file `jestconfig.json`:

```json
{
  "transform": {
    "^.+\\.(t|j)sx?$": "ts-jest"
  },
  "testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
  "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
  "testEnvironment": "jsdom"
}
```

Create folder `tests`.

Time to write our first test. We will write very simple test, which will check, that our render passed successful.

In folder `tests` create file `common.test.tsx`:

```js
import * as React from 'react'
import { render } from '@testing-library/react'

import 'jest-canvas-mock'

import { MyCounter } from '../src'

describe('Common render', () => {
  it('renders without crashing', () => {
    render(<MyCounter />)
  })
})
```

Change scripts section in `package.json`:

```bash
"test": "jest --config jestconfig.json"
```

```json
{
  "name": "my-react-typescript-package",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc",
    "lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\"",
    "prettier": "prettier --write \"{src,tests,example/src}/**/*.{js,ts,jsx,tsx}\"",
    "test": "jest --config jestconfig.json"
  },
  "keywords": [],
  "author": "gapon2401",
  "license": "ISC",
  "devDependencies": {
    "@testing-library/react": "^13.3.0",
    "@types/jest": "^28.1.1",
    "@types/react": "^18.0.12",
    "@typescript-eslint/eslint-plugin": "^5.27.1",
    "@typescript-eslint/parser": "^5.27.1",
    "eslint": "^8.17.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-react": "^7.30.0",
    "eslint-plugin-react-hooks": "^4.5.0",
    "jest": "^28.1.1",
    "jest-canvas-mock": "^2.4.0",
    "jest-environment-jsdom": "^28.1.1",
    "prettier": "^2.6.2",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "ts-jest": "^28.0.4",
    "typescript": "^4.7.3"
  }
}
```

Run `yarn test`. You should pass the test:

![Test passed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u0pkho1fnm70h07m8417.jpg)

Compare your project structure after step 6:

![Project structure after step 6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2ytfcbxrw94qlbjbgcm.jpg)

---

### Step 7. Configure package.json and prepare for publishing

I suggest to split our `build`. We will compile not only the ECMAScript module, but also the CommonJs module to make our package as accessible as possible.

In `package.json` replace:

```bash
"build": "tsc",
```

With the following code:

```bash
"build": "yarn build:esm && yarn build:cjs",
"build:esm": "tsc",
"build:cjs": "tsc --module commonjs --outDir dist/cjs",
```
We added `build:esm` and `build:cjs` and combined them in one command.

Run `yarn build` and you will see, that our `dist/` now has also a `cjs` folder.

Go further.

In `package.json` we can use magic scripts that are automatically called when the package is published. They will help us to check our package for all kinds of errors and not accidentally upload an update that will crash hundreds of thousands of projects in which our package will be used! 😜

- `prepare` — runs BEFORE the package is packed and published. Runs on local npm install without any arguments,
- `prepublishOnly` — runs BEFORE the package is prepared and packed, ONLY on npm publish. Here will be our tests.

Add to `scripts` section:

```bash
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run prettier && npm run lint"
```
Change the `version`, `description`.

Tell npm where it can import the main file of our project from, and also where all the types are located:

Replace:

```bash
"main": "index.js",
```

With:

```bash
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/esm/index.d.ts",
```

Add information about remote git repository (specify your git url from step 3):

```bash
"repository": {
  "type": "git",
  "url": "git+https://github.com/gapon2401/my-react-typescript-package.git"
},
```

Specify that the project that will use our package must have a react version of at least >=16:

```bash
"peerDependencies": {
  "react": ">=16"
},
```

To ensure that your package does not have any redundant files, use only allowed files and folders that will be added to the package:

```bash
"files": [
  "dist",
  "LICENSE",
  "README.md"
],
```

Add keywords so everyone can find your project:

```bash
"keywords": [
  "react",
  "typescript",
  "awesome-project"
],
```

Specify your license:

```bash
"license": "MIT",
```

Don’t forget to change the `author`:

```bash
"author": "Igor Gaponov (gapon2401)",
```

I think that’s enough.)

Add description to your `README.md` file.

For now I have only one row there. This is the h1:

```
# my-react-typescript-package
```

Take a look at the final version of `package.json`:

```json
{
  "name": "my-react-typescript-package",
  "version": "0.1.0",
  "description": "My first react typescript package",
  "main": "./dist/cjs/index.js",
  "module": "./dist/esm/index.js",
  "types": "./dist/esm/index.d.ts",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/gapon2401/my-react-typescript-package.git"
  },
  "scripts": {
    "build": "yarn build:esm && yarn build:cjs",
    "build:esm": "tsc",
    "build:cjs": "tsc --module commonjs --outDir dist/cjs",
    "lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\"",
    "prettier": "prettier --write \"{src,tests,example/src}/**/*.{js,ts,jsx,tsx}\"",
    "test": "jest --config jestconfig.json",
    "prepare": "npm run build",
    "prepublishOnly": "npm test && npm run prettier && npm run lint"
  },
  "peerDependencies": {
    "react": ">=16"
  },
  "files": [
    "dist",
    "LICENSE",
    "README.md"
  ],
  "keywords": [
    "react",
    "typescript",
    "awesome-project"
  ],
  "author": "Igor Gaponov (gapon2401)",
  "license": "MIT",
  "devDependencies": {
    "@testing-library/react": "^13.3.0",
    "@types/jest": "^28.1.1",
    "@types/react": "^18.0.12",
    "@typescript-eslint/eslint-plugin": "^5.27.1",
    "@typescript-eslint/parser": "^5.27.1",
    "eslint": "^8.17.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-react": "^7.30.0",
    "eslint-plugin-react-hooks": "^4.5.0",
    "jest": "^28.1.1",
    "jest-canvas-mock": "^2.4.0",
    "jest-environment-jsdom": "^28.1.1",
    "prettier": "^2.6.2",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "ts-jest": "^28.0.4",
    "typescript": "^4.7.3"
  }
}
```

---

### Step 8. Commit and push your code

First of all, let’s connect remote and local repositories and push our project.

Run the following code:

```
git add .
git commit -m "Initial commit"
git remote add origin <Repository Url>
git push -u origin master
```

Don’t forget to change `<Repository Url>` on yours. It can be `https` url, or `ssh`. This url you can get in your repository.

Take a look on examples, how it could be.

- HTTPS repository url (you will need to authorize)

```
git remote add origin https://github.com/gapon2401/my-react-typescript-package.git
```

- SSH repository url (you need to configure [connecting with ssh](https://docs.github.com/en/authentication/connecting-to-github-with-ssh))

```bash
git remote add origin git@github.com:gapon2401/my-react-typescript-package.git
```

I use the second solution.

---

### Step 9. Publishing to NPM

- **Choose the package name**

We have to check that the name of our package is free to use. Go to the website [https://www.npmjs.com/](https://www.npmjs.com/) and use search tool.

You can find and change default name of the project in `package.json`:

![package name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wwd7oru1b0duf88795sy.jpg)

In my case it `my-react-typescript-package`. There is no such package yet.

I prefer to check this way: insert the name directly to the link

```bash
https://www.npmjs.com/package/<Your package name>
```

Instead of `<Your package name>` use the name of your project.

I get the following url:

```bash
https://www.npmjs.com/package/my-react-typescript-package
```

If the name is free, you will see 404 page:

![Package name is free](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0hxgetf93fc22u785ekx.jpg)

- **Register/login to NPM**

In order to publish the project you need to authorize in [https://www.npmjs.com/](https://www.npmjs.com/).

If you don’t have account yet, go and register [https://www.npmjs.com/signup](https://www.npmjs.com/signup) or use command line:

```bash
npm adduser
```

You will be asked to enter `username`, `password` and `email`.

If you already have an account, run:

```bash
npm login
```

And complete the authorization.

- **Publishing**

Before publishing, I recommend you to check that you will have only the necessary files in the package, there are no errors, everything is formatted correctly. For this you can run the command:

```bash
npm publish --dry-run
```
Package **WILL NOT BE** published, don’t worry.

You will see, that the “magic script” `prepublishOnly` will be called and tests and code formatting will run.

Then you will get the list of all files, which will be included at the project:

![The list of all package files](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/862favk0jketxy3r2msz.jpg)

In `Tarball Contents` we have the files and folders, that were specified in `package.json`:

```bash
"files": [
  "dist",
  "LICENSE",
  "README.md"
],
```

Everything is fine, let’s publish the package!

Run the command:

```bash
npm publish
```

I always worry, when run this command 🤣

I am attaching the full result of the command call:

![The result of npm publish](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5nc0v1yxbe5adtzrut6l.jpg)

Now I can congratulate you!🎉 The package is published, it is public, it can be used by anyone! It wasn’t so hard 😉

Let’s take a look at the package on the NPM.

Open the link, we did it before.

```bash
https://www.npmjs.com/package/<Your package name>
```

![NPM page of the package](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8uij5unaa9oh2joverj.jpg)

Looks great! 🔥

---

### Step 10. Creating of the example folder

Let’s keep making our package better and better. In this step we will create an example folder in which we will show how we can work with our package. In this folder, you can quickly start the server, play around with the code.
To quickly launch the application, we will use [https://parceljs.org/](https://parceljs.org/).

Create a folder `example`.

Inside that folder create empty folder `src` and files:

- `.gitignore`:

```bash
node_modules
yarn.lock
.parcel-cache
dist
yarn-error.log
```

- `package.json`:

```json
{
  "name": "example",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "start": "parcel src/index.html"
  }
}
```

- `README.md`:

```
# How to start example page?

Then in the example directory, run all dependencies:

### `yarn install`

Then use the command:

### `yarn start`

Open [http://localhost:1234](http://localhost:1234) to view it in the browser.
```

Now pay attention! In the command line from the root of your project move into example folder:

```bash
cd example
```

Then run:

```bash
yarn add -D parcel my-react-typescript-package react react-dom @types/react-dom @types/react
```

Inside `src` folder create the file `index.html`:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta
      name="description"
      content="Example page for my-react-typescript-package"
    />
    <title>my-react-typescript-package</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <script type="module" src="index.tsx"></script>
    <div id="root"></div>
  </body>
</html>
```

Now we need to import our package and create the example of how to use it.

Inside `src` folder create `index.tsx` file:

```js
import React from 'react'
import ReactDOM from 'react-dom/client'
import { MyCounter } from 'my-react-typescript-package'

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
  <React.StrictMode>
    <div>
      <h2>Default counter</h2>
      <MyCounter />
    </div>
    <hr />
    <div>
      <h2>Counter with predefined value</h2>
      <MyCounter value={5} />
    </div>
  </React.StrictMode>,
)
```

Time to run the server! Use the command:

```bash
yarn start
```

You will have the following message after successfully starting the server:

![Parcel server started](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g7l5gc9j36g3vlwsofem.jpg)

Open the link [http://localhost:1234](http://localhost:1234) to watch the example:

![Package example of usage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8oo97l2j6owf47zziefp.jpg)

Compare the example folder structure, after all installations and server running, it should look like this:

![example folder structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ovy4lowx0t92f7qzqn3f.jpg)

---

### Step 11. Setup automated builds

Let’s get into automated builds. It’s rather inconvenient when you have to push changes to git, to npm with each release. We will automate this process with Github Actions.

![Github Actions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nj058be407xub1ap13cn.jpg)

Create at the root of the project structure the folder `.github`.

Inside it create the folder `workflows`.

Inside `workflows` create the file `publish.yml`:

```yaml
# This is a name of the workflow
name: build
# Controls when the workflow will run
on:
  # Triggers the workflow on published releases
  release:
    types: [published]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:

      - name: Checkout
        # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
        uses: actions/checkout@v3

      - name: Setup Node
        # Setup node environment
        uses: actions/setup-node@v3
        with:
          # Node version. Run "node -v" to check the latest version
          node-version: 16.x
          registry-url: https://registry.npmjs.org/

      - name: Install dependencies
        run: yarn && yarn install

      - name: Build
        run: yarn build

      - name: Publish
        run: yarn publish

        env:
          # We need this to our NPM account
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```

The main points in the file have been commented.

When we will release to github, our package will be automatically built and pushed to NPM.

I want you to look at the line:

```yaml
# We need this to our NPM account
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```

This is the way we are going to connect Github with NPM.

In browser open your account on [https://www.npmjs.com/](https://www.npmjs.com/), go to the page with tokens:

![NPM Access Tokens link](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fep36faz75x5dfuwm3aa.jpg)

Generate new token:

![Generate new NPM token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pdzvjdy6lu1vnnibv7is.jpg)

You will be prompted to enter its name and select the type. `Automation` is suitable for github-actions:

![New access token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjrlavtgahksbvtn6bno.jpg)

Copy your token and open the github repo.

Go to the tab `Settings`, open `Secrets — Actions` and create new repository secret variable. Give it a name `NPM_TOKEN` and paste inside the value of the token from NPM:

![Creating new repository secret variable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogioq203k74t0fi5rn1t.jpg)

Well done! Now it remains to create a new version of the project and push it to github.

Don’t forget before committing any changes to run:

```bash
yarn prepublishOnly
```

Just to make sure that your code will be nice and clean.

After you push the project, go to the Actions tab, where you will see that github has detected that you have workflow actions. But it hasn’t launched anything yet, because we haven’t made a release yet.)

![Github Actions workflows](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nk00z5ofot87nuzumqbc.jpg)

---

### Step 12. Demo page

We have already a folder with an example of using our package, but we would like to create a separate page so that the user can quickly see the package in action without having to clone the entire project. Let’s get on with this page!

We will use github-pages for creating the demo.

Create new git branch:

```bash
git checkout -b gh-pages
```

And now delete all the files from the project! 😱

Don’t worry, all previous files will stay safe in another `master` branch. The branch `gh-pages` will be used only for the demo, that’s why we need to delete all files. Come on, delete everything! 😈

Then in command line run:

```bash
npm init -y
```

This command will create file `package.json`. We will not configure it in detail this time.

For demo page I suggest using `parcel`, which is familiar for us ([step 10](#step-10-creating-of-the-example-folder))

Run:

```bash
yarn add -D parcel my-react-typescript-package react react-dom @types/react-dom @types/react typescript
```

Create file `.gitignore`:

```bash
node_modules
yarn.lock
.parcel-cache
dist
yarn-error.log
```

Create file `tsconfig.json` with contents:

```json
{
  "include": ["src"],
  "exclude": [
    "dist",
    "node_modules"
  ],
  "compilerOptions": {
    "module": "esnext",
    "lib": ["dom", "esnext"],
    "importHelpers": true,
    "declaration": true,
    "sourceMap": true,
    "rootDir": "./src",
    "outDir": "./dist/esm",
    "strict": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "moduleResolution": "node",
    "jsx": "react",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
  }
}
```

Follow the [step 4](#step-4-configure-eslint) to configure the Eslint.

Create empty folder `src`. Inside it create file `index.html` with the contents:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta
        name="description"
        content="Demo page for my-react-typescript-package"
    />
    <title>Demo page of my-react-typescript-package</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<script type="module" src="./index.tsx"></script>
<div id="root"></div>
</body>
</html>
```

Create file `index.tsx`:

{% gist https://gist.github.com/gapon2401/ce3e770ca63ff1da02006c940a27b61b %}

Add to `package.json` inside `scripts` section:

```bash
"start": "parcel src/index.html",
"build": "parcel build src/index.html --dist-dir docs --public-url .",
```

Take a look at my `package.json`:

```json
{
  "name": "my-react-typescript-package",
  "version": "0.1.0",
  "description": "Demo page of my-react-typescript-package",
  "scripts": {
    "start": "parcel src/index.html",
    "build": "parcel build src/index.html --dist-dir docs --public-url .",
    "lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\""
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/gapon2401/my-react-typescript-package.git"
  },
  "author": "gapon2401",
  "license": "MIT",
  "devDependencies": {
    "@types/react": "^18.0.14",
    "@types/react-dom": "^18.0.5",
    "@typescript-eslint/eslint-plugin": "^5.29.0",
    "@typescript-eslint/parser": "^5.29.0",
    "eslint": "^8.18.0",
    "eslint-plugin-react": "^7.30.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "my-react-typescript-package": "^0.1.0",
    "parcel": "^2.6.2",
    "process": "^0.11.10",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "^4.7.4"
  }
}
```

Run the server to make sure, that everything works fine:

```bash
yarn start
```

Now run the code checking:

```bash
yarn lint
```

No errors! 🤗 Great! It’s time to build the page.

```bash
yarn build
```

You should see the folder `docs` with all files of the demo project.

Here is the structure of the branch `gh-pages`:

![Structure of the gh-pages branch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hgsq0jkl40r5gztvkvzv.jpg)

Now let’s push all the changes to git. Run:

```bash
git add .
git commit -m "Initial pages commit"
git push -u origin gh-pages
```

Open your github repository and go to `Settings — Pages`. In the `source` select `/docs` and press `Save`:

![Github pages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94wunl20yr68ifxuncbv.jpg)

Wait about 3–5 minutes and your site will be available on the specified url, which is on your page:

![Link of the demo page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x2akvih9pfg3659nyjp3.jpg)

We have done with the demo page. As you see, you can customize it however you want and create a page or website of any complexity.

Here is the result: [https://gapon2401.github.io/my-react-typescript-package/](https://gapon2401.github.io/my-react-typescript-package/)

---

### Step 13. README.md file and shields.io

Now when we’ve done with all the code, we can move to the description file of the package.

Switch to `master` branch in git and open `README.md`.

Replace contents with the following code:

{% gist https://gist.github.com/gapon2401/79884ab8d77f15a2fe23f246dfc0cde9 %}

For description file we are using [markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet).

In `README.md` I have added a short description of the package, link to demo-page and a usage section.

At the beginning of the readme file I have small cute icons, this is [https://shields.io/](https://shields.io/). It is very convenient to have them. I’ve added just a couple as an example, you can use as many as you want.

[Add](https://img.shields.io/github-auth) `shields.io` to your github applications.

Take a look at the end of the file, I’ve created variables with images and links. Markdown allows us to make such variables, so the description file looks more accurate. Don’t forget to change the path to your repo and the package name.

Change version in `package.json`, make commit and push files to repository. Do everything by yourself, we’ve made it in previous steps.😉

---

### Step 14. Making release

This is the last step. It will be short 🙌

We have done everything, what we planned! Now we can make an official release on github and also check how automated builds work.

Go to the main page of the repository and click `Create a new release`:

![Create a new release](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d2r2aaeyopf8l6soctm9.jpg)

Specify your current version as a tag, it will be created automatically on release. Print release title and description and click `Publish release`.

![Publish release](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9v10djaeuw8kou5lwpzh.jpg)

Now open tab `Actions` and make sure, that your build was successfully done.

![Automated build on github actions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnwjs13u747nvha5thwo.jpg)

Let’s check, that package was updated in NPM.

Open the package link:

```bash
https://www.npmjs.com/package/<Your package name>
```

I will open mine:

```bash
https://www.npmjs.com/package/my-react-typescript-package
```

You should see a new version with new description:

![NPM updated package](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5olzhkltb9g6v4tmjkxn.jpg)

---

Congratulations! 🥳🎉👏 You are brilliant! 💎✨

It was a long journey, but I hope very useful. I tried to describe everything that I had difficulty with when I first published my package and collected this information piece by piece.

Thank you for reading! ❤️