Part 1: From Zero to Published β How I Built and Published My First React NPM Package
Learn how to build and publish your own NPM package with Rollup, testing, and troubleshooting. Stay tuned for part 2: building a React state management library!
Akash Deep Chitransh
Last Updated Feb 6, 2026

π§© Introduction
Recently, I built and published my own npm package — a minimal state management library for React. It’s tiny, fast, supports selectors for optimal re-renders, and works seamlessly with TypeScript.
My goal wasn't just to create another state library — I wanted to learn the complete journey of publishing a package to npm, from writing the logic to bundling it properly and finally seeing it live on npmjs.com. I also needed a lightweight state manager for my personal projects — something simple and custom-tailored.
In this blog, I’ll walk you through everything I learned while preparing, testing, and publishing a React package to npm.
β Why You Can’t Just Push Raw Code to NPM
When I first started, I assumed I could just write some React + TypeScript code and publish it directly to npm. But here's the reality:
NPM packages are meant to be consumed by many different projects, environments, and build systems — so they must be precompiled, optimized, and portable.
Here’s why raw code doesn’t work:
-
TypeScript Needs Compilation: You can't publish
.tsor.tsxfiles directly — they must be compiled to JavaScript. -
ES Modules, CJS, or UMD?: Different environments expect different module formats. Some tools prefer ESM (
import), others expect CJS (require). -
Multiple Files & Internal Structure: Consumers shouldn't need to know about your internal file structure — they just need a clean
dist/folder withindex.js,index.d.ts, etc. -
Tree-Shaking and Optimization: To reduce bundle size, your code needs to be treeshakable — meaning your package should only export what’s needed.
That’s where bundlers like Rollup come in — they take your raw TypeScript code and produce a clean, production-ready output.
π¦ Choosing a Bundler — Why I Picked Rollup
There are several bundlers out there like Webpack, Vite, and ESBuild. But for publishing libraries, Rollup stands out:
-
β Tree-shaking by default — keeps the bundle lean
-
β Simpler config for libraries compared to Webpack
-
β Great plugin ecosystem — especially for TypeScript and JSX
-
β Widely used for React/JS libraries (even used by major open-source projects)
So I chose Rollup to bundle my TypeScript React library into a small, optimized dist/ folder, ready for npm.
βοΈ Setting Up Rollup for Packaging
To bundle my React + TypeScript library, I used Rollup—a lightweight module bundler perfect for libraries. Here's what my rollup.config.js looked like:
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
import terser from "@rollup/plugin-terser";
export default [
{
input: "src/index.ts",
output: [
{
file: "dist/index.js",
format: "esm",
sourcemap: true,
},
],
plugins: [peerDepsExternal(), typescript(), terser()],
external: ["react", "react-dom"],
},
{
input: "src/index.ts",
output: {
file: "dist/index.d.ts",
format: "es",
},
plugins: [dts()],
},
];
π What Each Plugin Does
-
@rollup/plugin-typescript: Compiles.tsand.tsxfiles into JavaScript. -
rollup-plugin-peer-deps-external: Automatically markspeerDependencies(likereact) as external, so they aren't bundled. -
@rollup/plugin-terser: Minifies the final output to reduce bundle size. -
rollup-plugin-dts: Bundles all your.d.tsfiles into one cleanindex.d.tsfor type support.
π¦ Output
-
dist/index.js: Your bundled library code (ESM format). -
dist/index.d.ts: A single declaration file for TypeScript consumers. -
β Sourcemaps included for better debugging.
π¦ Crafting the Perfect package.json for an NPM Library
Your package.json is the blueprint of your library—it tells the world (and npm) how your package works, what it needs, and how to use it. Here's a breakdown of the important fields in mine:
π Key Fields
"name": "@akash_deep_chitransh/react-mini-state",
"version": "1.0.3",
"description": "A lightweight and minimal state management solution for small react apps.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist"],
"type": "module"
Β
-
name: Scoped name (@your-username/package-name) helps avoid name conflicts. -
version: Semver-compliant version number. -
description: Shows up on npm and GitHub. -
main: Entry point of the library. -
types: Entry point for TypeScript types. -
files: Ensures only thedistfolder gets published. -
type: module: Marks output as ES Modules (used with modern bundlers).
π Scripts
"scripts": {
"build": "rollup -c",
"test": "vitest run",
"test:watch": "vitest"
}
Β
-
build: Bundles the code using Rollup. -
test/test:watch: Runs unit tests using Vitest.
π€ Peer Dependencies
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
Β
This tells npm:
"Hey, the consuming project must install React—I'll use whatever version they have."
This avoids bundling React into your library, which can lead to duplicate React copies and broken apps.
π§ͺ Dev Dependencies
"devDependencies": {
...
"rollup": "^4.41.1",
"typescript": "^5.8.3",
"vitest": "^3.1.4",
...
}
Β
These are tools used only during development:
-
Rollup plugins for bundling
-
TypeScript for typing
-
Vitest + React Testing Library for unit testing
π Repository and Metadata
"repository": {
"type": "git",
"url": "https://github.com/CodeChitra/react-mini-state"
},
"license": "MIT",
"keywords": ["react", "state-management", "store", "typescript"]
Β
-
Helps developers find and contribute to your library.
-
Keywords improve discoverability on npm.
-
Open-source license is a must.
π§ͺ How to Test Your NPM Package Locally Before Publishing
Before pushing your package to the world, it’s critical to test it locally—both through automated unit tests and by consuming it in a real app.
β 1. Write Unit Tests with Vitest
We used Vitest because it’s fast, simple, and works great with TypeScript and React.
npm run test # Run all tests once
npm run test:watch # Re-run tests on file changes
Make sure your test files are placed correctly (__tests__, .test.ts, etc.), and your assertions verify your library's core logic (e.g., store creation, state updates, selectors).
π 2. Test in a Real React App Using npm link
Sometimes unit tests aren't enough. You want to see how the package behaves inside a real React app. That’s where npm link helps.
π¦ In your library project
npm run build # Ensure the dist folder is ready
npm link # Makes your local package globally linkable
π In your test React app
npm link @akash_deep_chitransh/react-mini-state
Now your test app will use the local version of your package—perfect for development and debugging.
Tip: After making changes in your package, just re-run
npm run buildand refresh your app!
π Undoing the Link
Once you're done testing:
npm unlink @akash_deep_chitransh/react-mini-state
npm install # Reinstalls the original dependency if needed
π Publishing to NPM (and the Issues I Faced)
Once I verified everything was working locally, it was time to share my library with the world via npm. Here’s how I did it — along with a few common gotchas you might face too.
π Step 1: Update package.json
Make sure you’ve set these fields correctly:
{
"name": "@akash_deep_chitransh/react-mini-state",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist"],
"license": "MIT",
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
}
Β
-
name: Scoped name (@your-username/package-name) helps avoid conflicts. -
files: Ensures only thedistfolder is published. -
main/types: Points to the correct entry file and declarations.
π Step 2: Login to NPM
npm login
Β
Enter your username, password, and email.
π¦ Step 3: Publish the Package
npm publish --access public
Β
-
Use
--access publicif you're publishing a scoped package (@scope/package-name), or it will fail with a 403.
β Common Errors I Faced
1. 403 Forbidden Errors
npm ERR! 403 Forbidden - You do not have permission to publish "package-name"
-
The package name may already be taken. Try using a scoped name like
@your-username/package-name.
2. Invalid Tag Name
npm ERR! Invalid tag name "^>=18.0.0"
-
Caused by incorrect
peerDependenciesversion syntax. -
Fix: use valid semver:
β"react": ">=18.0.0"
β"react": "^>=18.0.0"
β Success!
Once published, your package will be available at:
https://www.npmjs.com/package/@akash_deep_chitransh/react-mini-state
You can install it like this:
npm install @akash_deep_chitransh/react-mini-state
π§ Conclusion & Key Takeaways
Building and publishing my own NPM package was an incredibly rewarding experience. Not only did I learn how to structure and bundle a reusable library, but I also got hands-on with tools like Rollup, TypeScript, and Vitest—and even tackled real-world issues like dependency management and NPM errors.
Here are the key lessons I took away:
-
β Keep your
package.jsonclean and precise – especially when it comes tomain,types, andpeerDependencies. -
π§© Bundlers like Rollup help create optimized builds and clean
.d.tsfiles, critical for library consumers. -
π Test locally before publishing using
npm linkor local installs. -
π« Expect some publishing hurdles, especially with permissions and naming. But every error is a learning moment.
This blog was just the first part. In the next one, I’ll dive into how I actually built my own React state management library, including the API design, React hooks, selectors, and how it compares to other tools like Zustand and Redux.
Thanks for reading! π
If you found this helpful or are building something similar, let’s connect on LinkedIn.
Β Learn Next
Comments
Be the first to share your thoughts!
No comments yet.
Start the conversation!
Share your expertise
Publish a blog or quick notes on topics you know well β your write-up could be the answer someone needs before their next frontend interview.
Build your portfolio
Help the community
Sharpen your skills
Earn goodies
Other Related Blogs
How does JWT (JSON Web Token) Authentication work - Pros & Cons
Frontendgeek
Last Updated Jun 15, 2026
Understand the JWT(JSON Web Token) and how JWT decode works. It also covers how the end-to-end JWT authentication works between client & server, along with the pros and cons of using JWT.
What is CORS ? Cross-Origin Resource Sharing Explained [For Interviews]
Anuj Sharma
Last Updated Feb 6, 2026
A brief explanation of Cross-Origin Resource Sharing (CORS) concept to enable client application accessing resources from cross domain and HTTP headers involved to enable resource access.
Understand JavaScript Local Storage, Session Storage and Cookies
Anuj Sharma
Last Updated Feb 6, 2026
Explore how to create and use javascript local storage, session storage and cookies. Explore the key differences between Local Storage vs Session Storage vs Cookies to understand the trade-offs.
Understanding Critical Rendering Path (CRP) to Improve Web Performance
Anuj Sharma
Last Updated Feb 6, 2026
Understand what all steps are involved in the Critical Rendering Path (CRP) and how optimization of different steps can improve the overall web-vitals of a web application
What happens when you type google.com in the browser
Anuj Sharma
Last Updated Feb 5, 2026
Details about how the browser works behind the scenes and what happens when you type google.com in the browser, starting from communication to the webpage rendering.
Boost Your Site Speed with CSS Sprites: A Practical Guide
Vaibhav Kumar
Last Updated Jan 28, 2026
Master CSS sprites to slash HTTP requests, supercharge load times, and optimize iconsβpractical guide with code, tools, and 2026 best practices.
Explained Web Authorization Techniques - Session & JWT
Anuj Sharma
Last Updated Dec 16, 2025
Understand important web authorization techniques to enhance role-based authentication for any web application with popular techniques like Session & JSON Web Token (JWT)
