How to Create a React NPM Package with Typescript



Photo by Kelly Sikkema on Unsplash

Npm packages allow us to create re-usable code modules that we can use in other applications. Often, while using React, I find myself repeating code and re-using the same logic. Creating an npm package means that this can be written once, and used anywhere.

This article will walk through how to create a React npm package with Typescript, and most importantly, how to get it published. We can then import this into any project, and use it to our heart’s content. 

Prerequisites


Project Setup

Photo by Kelly Sikkema on Unsplash

We first want to create a new folder to house our package. Run the following, replacing <packagename> with your chosen name:

mkdir <packagename> && cd <packagename>

Git Setup (optional)

From here, we can initialise a Git repository. While this is recommended, this step is optional. Run the following command:

git init

We now want to create a .gitignore file to avoid committing certain directories.

Create a new file called .gitignore with the following contents:

# .gitignore
node_modules
.vscode

NPM Setup

Now we can setup npm. Run the following to setup our npm package, leaving the defaults for each prompt except entry point, for which we want to enter index.ts:

npm init

The output should look like the following:

package name: (<packagename>) 
version: (1.0.0) 
description: 
entry point: (index.js) index.ts
test command: 
git repository: 
keywords: 
author: 
license: (ISC)

This will create a basic package.json file, which describes our npm package.

Setting up Typescript

With our npm package created, we want to install typescript:

npm install --save-dev typescript

This will install typescript as a dev dependency, which means it won’t be included in our final package, as it is not necessary once our code has built.

We now need to setup the typescript compiler:

npx tsc --init

This will create a tsconfig.json file which contains any typescript compilation options.

Replace the contents of the file with the following:

{
  "include": ["src"], /* Include only the src directory */
  "compilerOptions": {
    "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
    "module": "commonjs" /* Specify what module code is generated. */,
    "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
    "outDir": "dist",
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
    "strict": true /* Enable all strict type-checking options. */,
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  }
}

This will ensure our typescript files are generated correctly for our package and in the right location.

Package.json Setup

We now want to make a few changes to package.json

Firstly, we want to only include our /dist files in our package. Then, we want to include React as a peerDependency.

We also want to create a build script to compile our code. F

inally, we need to export the correct files so we can import them from other projects.

Our package.json file should look like the following:

{
  "name": "<packagename>",
  "version": "1.0.0",
  "description": "",
  "main": "dist/index.js", // Set compiled file as main
  "files": [
    "dist" // Only export dist directory
  ],
  "exports": { // Setup exports with typing
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  },
  "scripts": {
    "build": "tsc" // Build our project
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "typescript": "^5.1.6"
  },
  "peerDependencies": {
    "react": "^18.2.0" // Include react as a peer dependency
  }
}

Write our Code

Photo by Kelly Sikkema on Unsplash

With all the setup done, we can now start writing the bulk of our application.

Create a new folder called src. Within this folder, create a file called index.ts. This will be the entry point for our package. From here, we can import functions from other files, as well as export anything we want to be available from our package.

For the purposes of this tutorial, we’ll create just a simple function to export. Add the following to index.ts:

/* src/index.ts */
export const testPackage = () => {
    return "Hello World!"
}

With that, our package is almost complete. We can now move on to publishing…

Publishing our Package

Photo by AbsolutVision on Unsplash

Publishing allows us and others to use our package within their applications. Fortunately, this is very simple. Firstly, we want to build the latest version of our package:

npm run build

This will compile and setup our code for publishing.

Next, run the following to push our code to the npm repository.

npm publish

If it asks you to sign in, follow the instructions to authenticate your terminal with npm.

And that’s it! We’ve published our first npm package.

Using our Package

We can install our package from any react project with:

npm i <packagename>

From here, we can use our new package within any component:

import { testPackage } from "<packagename>"

const test = () => {
  console.log(testPackage());

  return <></>
}

Extensions 

Photo by Steve Johnson on Unsplash

To make package development easier, there are a couple things we can add to our package.json to speed things up.

Build cleanup

Currently running npm run build simply builds on top of everything already within the /dist folder. 

To clean this up, we can use the rimraf package. This adds cross platform support for removing directories. 

Install rimraf with:

npm install rimraf

We can then add this to our build step by replacing scripts in package.json with :

"scripts": {
    "clean": "rimraf dist",
    "prebuild": "npm run clean",
    "build": "tsc",
  },

Not only does this add the clean command, calling our script ‘prebuild’ ensures that it gets run every time before build.

Running npm run build, we can see that our /dist folder is removed and replaced during the build process.

Automatic Publish Builds

Whenever we publish a new version of our package to npm, we want to be confident we’re publishing the latest version of our code. 

As of now, we have to manually remember to build our code before running npm publish

Being someone who often forgets to build before publishing, it would be pretty useful to have this done automatically. Fortunately, we can use a technique from before, involving “pre-”. 

Add the following lines to the scripts section of package.json:

"preversion": "npm run build",
"version": "npm publish"

We can now run npm run version to both build a new version of our code, and publish it to the npm registry.


Finishing Up

Using this as a starting point, we can now create packages for any code we re-use frequently. We can make anything from custom hooks to customisable re-usable components. Check out the full code for the sample package here: https://github.com/oflint-1/npm-package-tutorial