Publish a React and Typescript Component as NPM Package

Roadside CoderRoadside Coder
4 min read

Have you ever wondered how libraries like Material UI and Tailwind CSS are built and published? Trust me, it's much easier than you think. In this guide, we'll walk through the process of taking our toast component and publishing it to NPM.

Publishing your own libraries can enhance your resume, potentially landing you a job or freelance clients. It’s a valuable skill that looks impressive in your portfolio.

If you're someone who likes to watch videos more, Here's the video version of this blog -

What We'll Cover

  • Introduction to the React TypeScript NPM library we'll create

  • Explanation of NPM (Node Package Manager) and what an NPM library is

  • Setting up the project

  • Compiling TypeScript files

  • Bundling using Rollup

  • Publishing to NPM

Setting up the Project

We have a pre built React with TypeScript Toast component. You can watch the full video of building this component here.

  1. Create a new folder for your project.

  2. Copy thesrc folder from your toast component and delete all global files and create an index.ts file exporting our component.

     import useNotification from "./hooks/useNotification";
     export default useNotification;
    

    It should look something like this -

  3. Install necessary dependencies like React, TypeScript, and tslib.npm i react typescript @types/react tslib --save-dev

     npm i react typescript @types/react tslib --save-dev
    

Compiling TypeScript Files

Create a tsconfig.json file by running:

npx tsc -init

Here is the essential configuration we need in tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"]
}

Explanation of Key Options

  • "target": "es5": Ensures compatibility with older browsers.

  • "lib": ["dom", "dom.iterable", "esnext"]: Includes necessary libraries.

  • "allowJs": true: Allows processing of JavaScript files.

  • "skipLibCheck": true: Skips type checks for libraries.

  • "esModuleInterop": true: Enables ES module interoperability.

  • "allowSyntheticDefaultImports": true: Allows default imports.

  • "strict": true: Enables strict type-checking.

  • "forceConsistentCasingInFileNames": true: Enforces consistent file name casing.

  • "noFallthroughCasesInSwitch": true: Prevents fall-through cases in switch statements.

  • "module": "esnext": Uses ESNext module system.

  • "moduleResolution": "node": Uses Node.js module resolution.

  • "resolveJsonModule": true: Resolves JSON modules.

  • "isolatedModules": true: Isolates each module.

  • "noEmit": true: Prevents emitting JavaScript files.

  • "jsx": "react-jsx": Uses React JSX factory.

Bundling Using Rollup

Now that we have compiled our code, we need to bundle it for different environments.

( Bdw, I have recently released a Complete Frontend Interview Prep Course, you might wanna check it out here - https://roadsidecoder.com/course-details )

  1. Install Rollup:

     npm i rollup
    
  2. Install additional plugins:

     npm i @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript rollup-plugin-peer-deps-external @rollup/plugin-terser rollup-plugin-dts --save-dev
    
    • @rollup/plugin-node-resolve: Resolves dependencies in your code.

    • @rollup/plugin-commonjs: Allows use of CommonJS modules.

    • @rollup/plugin-typescript: Enables TypeScript support.

    • rollup-plugin-peer-deps-external: Manages peer dependencies.

    • @rollup/plugin-terser: Minifies JavaScript code.

    • rollup-plugin-dts: Generates type declarations.

  3. Createrollup.config.js:

    • Add following keys to package.json
    {
      ...
      "main": "dist/index.js",
      "module": "dist/index.mjs",
      "types": "dist/index.d.ts",
      ...
    }
  • Writing the config (rollup.config.js) -
    import resolve from "@rollup/plugin-node-resolve";
    import commonjs from "@rollup/plugin-commonjs";
    import typescript from "@rollup/plugin-typescript";
    import dts from "rollup-plugin-dts";
    import terser from "@rollup/plugin-terser";
    import peerDepsExternal from "rollup-plugin-peer-deps-external";

    const packageJson = require("./package.json");

    export default [
      {
        input: "src/index.ts",
        output: [
          {
            file: packageJson.main,
            format: "cjs",
            sourcemap: true,
          },
          {
            file: packageJson.module,
            format: "esm",
            sourcemap: true,
          },
        ],
        plugins: [
          peerDepsExternal(),
          resolve(),
          commonjs(),
          typescript({ tsconfig: "./tsconfig.json" }),
          terser(),
        ],
        external: ["react", "react-dom"],
      },
      {
        input: "src/index.ts",
        output: [{ file: packageJson.types }],
        plugins: [dts.default()],
      },
    ];
  • format: "cjs": Output as CommonJS module.

  • format: "esm": Output as ES module.

  • external: Excludes specified dependencies from the bundle.

  1. Build Script:

     {
       ...
       "scripts": {
         "rollup": "rollup -c --bundleConfigAsCjs",
         ...
       }
     }
    

    Explanation of the Build Script

    • "rollup": This is the name of the script. You can run it using npm run rollup.

    • "rollup -c --bundleConfigAsCjs": This command runs Rollup with the specified options:

      • -c: Tells Rollup to use a configuration file (rollup.config.js by default).

      • --bundleConfigAsCjs: Ensures the output is bundled as a CommonJS module.

  2. Handling CSS:

    • Install the postcss plugin:
    npm i rollup-plugin-postcss
  • Update rollup.config.js:
    import postcss from "rollup-plugin-postcss";

    ...
    plugins: [
      ...
      postcss(),
    ],
    external: [/\.css$/]
    ...

Here's our final rollup configuration file -

    import resolve from "@rollup/plugin-node-resolve";
    import commonjs from "@rollup/plugin-commonjs";
    import typescript from "@rollup/plugin-typescript";
    import dts from "rollup-plugin-dts";
    import terser from "@rollup/plugin-terser";
    import peerDepsExternal from "rollup-plugin-peer-deps-external";

    import postcss from "rollup-plugin-postcss";

    const packageJson = require("./package.json");

    export default [
      {
        input: "src/index.ts",
        output: [
          {
            file: packageJson.main,
            format: "cjs",
            sourcemap: true,
          },
          {
            file: packageJson.module,
            format: "esm",
            sourcemap: true,
          },
        ],
        plugins: [
          peerDepsExternal(),
          resolve(),
          commonjs(),
          typescript({ tsconfig: "./tsconfig.json" }),
          terser(),
          postcss(),
        ],
        external: ["react", "react-dom"],
      },
      {
        input: "src/index.ts",
        output: [{ file: packageJson.types }],
        plugins: [dts.default()],
        external: [/\.css$/],
      },
    ];

Publish to NPM

Finally, you can publish your package to NPM by following these steps:

  1. Log in to NPM:

     npm login
    
  2. Publish the package:

     npm publish
    

And that's it! You've successfully created and published a React TypeScript component to NPM.

Here's the library I published - https://www.npmjs.com/package/react-toast-popup

8
Subscribe to my newsletter

Read articles from Roadside Coder directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Roadside Coder
Roadside Coder