Skip to main content

Command Palette

Search for a command to run...

[Part 1] - Build & Publish React component as NPM package using Typescript compiler

Published
[Part 1] - Build & Publish React component as NPM package using Typescript compiler
A

Frontend Developer at Thoughtworks

I was too lazy to include webpack to bundle the react component as it needs a lot of boilerplate configuration code. so easiest way was to build the react typescript project using the typescript compiler with a little help from tsc-hooks.

you can follow the steps to know more about building and publishing a react project without the use of webpack.

Let's first initialise the project.

mkdir toggle-button

yarn init
or 
npm init

Add the following packages as dev dependencies-

yarn add -D react react-dom @types/react @types/react-dom typescript tsc-hooks
or 
npm install --save-dev react react-dom @types/react @types/react-dom typescript tsc-hooks

Update react as peer dependency so that the user of this package will be prompted to install react while adding this package.

now the package.json should look like -

package.json

{
  "name": "react-toggle-button",
  "version": "0.0.1",
  "description": "React toggle button",
  "main": "dist/", // update this
  "scripts": {
    "test": "yarn test",
    "build": "tsc" // runs tsc compiler to build the project in dist directory (configured in tsconfig.json)
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/YOUR-REPO/react-toggle-button.git"
  },
  "keywords": [
    "react",
    "react-toggle-button"
  ],
  "author": "AUTHOR NAME",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/YOUR-REPO/react-toggle-button/issues"
  },
  "homepage": "https://github.com/YOUR-REPO/react-toggle-button#readme",
  "devDependencies": {
    "@types/react": "^18.0.18",
    "react": "^18.2.0",
    "tsc-hooks": "^1.1.1", // needed to copy css files in the final build
    "typescript": "^4.8.3"
  },
  "peerDependencies": {
     "react": "^18.2.0" // add react as a peer dependency
  },
  "files": [
    "/dist" // tells the bundler to only add dist folder while publishing the package. package.json will be automatically added. 
  ]
}

Add tsconfig.json

now let's add tsconfig.json for the typescript compiler to know where and how to build the typescript files.

tsconfig.json

{
    "compilerOptions": {
        "target": "ES2015",
        "jsx": "react",
        "module": "esnext",
        "moduleResolution": "node",
        "allowJs": true,
        "declaration": true, // required to generate corresponding *.d.ts files
        "outDir": "./dist",
        "esModuleInterop": true,
        "strict": true,
        "noImplicitAny": true,
    },
    "exclude": [
        "node_modules",
    ],
    "hooks": [
        "copy-files" // copies all files to the dist folder
    ],
    "include": [
        "src/",
        "src/**/*.module.css" // include css files while building the project using tsc
    ]
}

if you want to avoid copying all files to the final build then another workaround is to use a package copyfiles and add a script in package.json to manually copy selective files. For eg. In this case we are copying all css files.

package.json

...
 "copy-files": "copyfiles -u 1 src/**/*.css dist/",
...

Add .npmignore

Add .npmignore to exclude src files from the final npm package build.

.npmignore

/src

Now lets add the react component in the src directory

src/ToggleButton/ToggleButton.tsx

import React from "react";
import styles from "./ToggleButton.module.css";
interface ToggleButtonProps {
  onClick: () => void;
  value: boolean;
}
const ToggleButton = ({ onClick, value }: ToggleButtonProps) => {
  function clickHandler() {
    onClick();
  }
  return (
    <div
      id={value ? "on" : "off"}
      className={styles.toggleButton}
      onClick={clickHandler}
    >
      <div className={styles.bar}>
        <div className={styles.circle}></div>
      </div>
    </div>
  );
};

export default ToggleButton;

src/ToggleButton/ToggleButton.module.css

.toggleButton {
  position: relative;
  width: 36px;
  height: 1rem;
  display: flex;
  align-items: center;
}
.circle {
  width: 17px;
  height: 17px;
  border-radius: 50%;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  transition: all 0.3s ease-in;
  --on : #eb5757;
  --off: #e0e0e0;
}
.bar {
  height: 20px;
  width: 100%;
  border-radius: 50px;
  padding: 2px;
}
#on .bar {
  background-color: var(--on);
}
#off .bar {
  background-color: var(--off);
}
#on .circle {
  background-color: var(--on);
  left: calc(100% - 19px);
}
#off .circle {
  background-color: var(--off);
  left: 2px;
}

Add declarations.d.ts

Add declarations.d.ts file so that typescript compliler know about the *.module.css files.

src/declarations.d.ts

declare module '*.module.css' {
  const classes: { [key: string]: string };
  export default classes;
}

Directory structure should look like -

Screenshot 2022-09-09 at 5.46.46 PM.png

Add Readme file

Don't forget to add a README.md file in the root directory with all the installation and usage of the package you are deploying. I am skipping this step in the example.

Build package & Publish

Lets run tsc compiler to generate the respective files in dist directory -

yarn build // spits the output files in the dist/ directory

now we are ready to publish the package. If you are deploying for the first time - create an npm account and login in the terminal.

npm login // skip this if you are already logged in
npm publish

Update the package version before every publish otherwise the publish will fail.

Thanks