DEV Community

Cover image for How to create and publish React Typescript npm package with demo and automated build
Igor
Igor

Posted on

How to create and publish React Typescript npm package with demo and automated build

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

Repo: https://github.com/gapon2401/my-react-typescript-package


We have 14 steps to deal with everything:

  1. Preparing the project
  2. Create your react application
  3. Configure git
  4. Configure ESLint
  5. Configure Prettier
  6. Adding tests with Jest
  7. Configure package.json and prepare for publishing
  8. Commit and push your code
  9. Publishing to NPM
  10. Creating of the example folder
  11. Setup automated builds
  12. Demo page
  13. README.md file and shields.io
  14. Making release

Step 1. Preparing the project

  • In command line of your project execute:
npm init -y
Enter fullscreen mode Exit fullscreen mode

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)
yarn add -D react react-dom typescript @types/react
Enter fullscreen mode Exit fullscreen mode

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
{
  "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,
  }
}
Enter fullscreen mode Exit fullscreen mode

You can take a look at all possible properties here: 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


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:

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
Enter fullscreen mode Exit fullscreen mode

This is our React application.

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

import MyCounter from './components/App'

export { MyCounter }
Enter fullscreen mode Exit fullscreen mode

It’s time to compile the project! 🤗

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

{
  "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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Run in terminal:

yarn build
Enter fullscreen mode Exit fullscreen mode

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


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

git init
Enter fullscreen mode Exit fullscreen mode

Create file .gitignore in project root with contents:

node_modules
.idea
dist
yarn-error.log
Enter fullscreen mode Exit fullscreen mode

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. 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:

yarn add -D eslint eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser
Enter fullscreen mode Exit fullscreen mode

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

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

{
  "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
  }
}
Enter fullscreen mode Exit fullscreen mode

Add .eslintignore:

node_modules
dist
Enter fullscreen mode Exit fullscreen mode

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

Change package.json, add to scripts section:

"lint": "eslint \"{**/*,*}.{js,ts,jsx,tsx}\""
Enter fullscreen mode Exit fullscreen mode

Here is my package.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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, run:

yarn lint
Enter fullscreen mode Exit fullscreen mode

You shouldn’t have any mistakes.

Compare your project structure after step 4:

Project structure after step 4


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:

yarn add -D eslint-config-prettier eslint-plugin-prettier prettier
Enter fullscreen mode Exit fullscreen mode

In project root create .prettierrc.json:

{
  "bracketSpacing": true,
  "singleQuote": true,
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": false,
  "printWidth": 120,
  "jsxSingleQuote": true,
  "endOfLine": "auto"
}
Enter fullscreen mode Exit fullscreen mode

Read more about all options: https://prettier.io/docs/en/options.html.

Add prettier plugin to .eslintrc:

{
  "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
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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

"prettier": "prettier --write \"{src,tests,example/src}/**/*.{js,ts,jsx,tsx}\""
Enter fullscreen mode Exit fullscreen mode

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

yarn prettier

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:

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

Compare your project structure after step 5:

Project structure after step 5


Step 6. Adding tests with Jest

For creating tests I’m using Jest 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:

yarn add -D jest jest-canvas-mock jest-environment-jsdom ts-jest @types/jest @testing-library/react
Enter fullscreen mode Exit fullscreen mode

Create at the project root file jestconfig.json:

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

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:

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 />)
  })
})
Enter fullscreen mode Exit fullscreen mode

Change scripts section in package.json:

"test": "jest --config jestconfig.json"
Enter fullscreen mode Exit fullscreen mode
{
  "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"
  }
}
Enter fullscreen mode Exit fullscreen mode

Run yarn test. You should pass the test:

Test passed

Compare your project structure after step 6:

Project structure after step 6


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:

"build": "tsc",
Enter fullscreen mode Exit fullscreen mode

With the following code:

"build": "yarn build:esm && yarn build:cjs",
"build:esm": "tsc",
"build:cjs": "tsc --module commonjs --outDir dist/cjs",
Enter fullscreen mode Exit fullscreen mode

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:

"prepare": "npm run build",
"prepublishOnly": "npm test && npm run prettier && npm run lint"
Enter fullscreen mode Exit fullscreen mode

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:

"main": "index.js",
Enter fullscreen mode Exit fullscreen mode

With:

"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/esm/index.d.ts",
Enter fullscreen mode Exit fullscreen mode

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

"repository": {
  "type": "git",
  "url": "git+https://github.com/gapon2401/my-react-typescript-package.git"
},
Enter fullscreen mode Exit fullscreen mode

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

"peerDependencies": {
  "react": ">=16"
},
Enter fullscreen mode Exit fullscreen mode

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

"files": [
  "dist",
  "LICENSE",
  "README.md"
],
Enter fullscreen mode Exit fullscreen mode

Add keywords so everyone can find your project:

"keywords": [
  "react",
  "typescript",
  "awesome-project"
],
Enter fullscreen mode Exit fullscreen mode

Specify your license:

"license": "MIT",
Enter fullscreen mode Exit fullscreen mode

Don’t forget to change the author:

"author": "Igor Gaponov (gapon2401)",
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Take a look at the final version of package.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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
git remote add origin git@github.com:gapon2401/my-react-typescript-package.git
Enter fullscreen mode Exit fullscreen mode

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/ and use search tool.

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

package name

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

https://www.npmjs.com/package/<Your package name>
Enter fullscreen mode Exit fullscreen mode

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

I get the following url:

https://www.npmjs.com/package/my-react-typescript-package
Enter fullscreen mode Exit fullscreen mode

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

Package name is free

  • Register/login to NPM

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

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

npm adduser
Enter fullscreen mode Exit fullscreen mode

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

If you already have an account, run:

npm login
Enter fullscreen mode Exit fullscreen mode

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:

npm publish --dry-run
Enter fullscreen mode Exit fullscreen mode

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

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

"files": [
  "dist",
  "LICENSE",
  "README.md"
],
Enter fullscreen mode Exit fullscreen mode

Everything is fine, let’s publish the package!

Run the command:

npm publish
Enter fullscreen mode Exit fullscreen mode

I always worry, when run this command 🤣

I am attaching the full result of the command call:

The result of npm publish

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.

https://www.npmjs.com/package/<Your package name>
Enter fullscreen mode Exit fullscreen mode

NPM page of the package

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/.

Create a folder example.

Inside that folder create empty folder src and files:

  • .gitignore:
node_modules
yarn.lock
.parcel-cache
dist
yarn-error.log
Enter fullscreen mode Exit fullscreen mode
  • package.json:
{
  "name": "example",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "start": "parcel src/index.html"
  }
}
Enter fullscreen mode Exit fullscreen mode
  • 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.
Enter fullscreen mode Exit fullscreen mode

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

cd example
Enter fullscreen mode Exit fullscreen mode

Then run:

yarn add -D parcel my-react-typescript-package react react-dom @types/react-dom @types/react
Enter fullscreen mode Exit fullscreen mode

Inside src folder create the file index.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>
Enter fullscreen mode Exit fullscreen mode

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

Inside src folder create index.tsx file:

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>,
)
Enter fullscreen mode Exit fullscreen mode

Time to run the server! Use the command:

yarn start
Enter fullscreen mode Exit fullscreen mode

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

Parcel server started

Open the link http://localhost:1234 to watch the example:

Package example of usage

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

example folder structure


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

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

Inside it create the folder workflows.

Inside workflows create the file publish.yml:

# 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 }}
Enter fullscreen mode Exit fullscreen mode

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:

# We need this to our NPM account
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

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

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

NPM Access Tokens link

Generate new token:

Generate new NPM token

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

New access token

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

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:

yarn prepublishOnly
Enter fullscreen mode Exit fullscreen mode

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


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:

git checkout -b gh-pages
Enter fullscreen mode Exit fullscreen mode

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:

npm init -y
Enter fullscreen mode Exit fullscreen mode

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)

Run:

yarn add -D parcel my-react-typescript-package react react-dom @types/react-dom @types/react typescript
Enter fullscreen mode Exit fullscreen mode

Create file .gitignore:

node_modules
yarn.lock
.parcel-cache
dist
yarn-error.log
Enter fullscreen mode Exit fullscreen mode

Create file tsconfig.json with contents:

{
  "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,
  }
}
Enter fullscreen mode Exit fullscreen mode

Follow the step 4 to configure the Eslint.

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

<!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>
Enter fullscreen mode Exit fullscreen mode

Create file index.tsx:

Add to package.json inside scripts section:

"start": "parcel src/index.html",
"build": "parcel build src/index.html --dist-dir docs --public-url .",
Enter fullscreen mode Exit fullscreen mode

Take a look at my package.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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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

yarn start
Enter fullscreen mode Exit fullscreen mode

Now run the code checking:

yarn lint
Enter fullscreen mode Exit fullscreen mode

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

yarn build
Enter fullscreen mode Exit fullscreen mode

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

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

git add .
git commit -m "Initial pages commit"
git push -u origin gh-pages
Enter fullscreen mode Exit fullscreen mode

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

Github pages

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

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/


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:

For description file we are using markdown.

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/. 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 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

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

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

Automated build on github actions

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

Open the package link:

https://www.npmjs.com/package/<Your package name>
Enter fullscreen mode Exit fullscreen mode

I will open mine:

https://www.npmjs.com/package/my-react-typescript-package
Enter fullscreen mode Exit fullscreen mode

You should see a new version with new description:

NPM updated package


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! ❤️

Top comments (0)