DesignDev.io logoDesignDev.io logo

  1. Home
  2. /Building a React UI Library from Scratch
  3. /Publishing Your First React Component to npm — the Complete Setup
← Back to home

Publishing Your First React Component to npm — the Complete Setup

Muhammad Athar

Muhammad Athar

Cover Image for Publishing Your First React Component to npm — the Complete Setup
Muhammad Athar

Muhammad Athar

April 1, 2026·4 min read
Updated April 30, 2026
Series:Building a React UI Library from Scratch
Level:Intermediate
Tags:npmpublishingreacttypescriptui library
Assumes:Completed articles 1–3, has an npm account or is ready to create one

Most tutorials stop at "it works on my machine." This one doesn't. Publishing to npm is where your library becomes real — installable, versioned, and usable by anyone. Here's the complete setup, including the fields most developers get wrong in package.json and how to inspect what you're actually shipping before it goes live.


There's a gap between "I built a component" and "I published a component." It's smaller than most developers think, but the details matter. A badly configured package.json means consumers get the wrong file, or no types, or a bundled copy of React that breaks their app. A missing .npmignore means your test files and Storybook stories go out with the package.

This article closes that gap. By the end, you'll have published v0.1.0 of your library and you'll understand every field that made it work.


The package.json fields that matter

Most developers treat package.json as boilerplate. For a library, it's configuration that directly affects how consumers import and use your code. Let's go field by field.

Here's what each field is doing:

name — use a scoped name (@your-username/ui-lib) if you want to namespace it under your npm username. Scoped packages can be published as public for free.

main — the CJS entry point. Used by older Node-based toolchains and bundlers that don't support ESM yet.

module — the ESM entry point. Used by modern bundlers like Vite and webpack 5. This is what most of your consumers will use.

types — points to the TypeScript declaration file. Without this, consumers get no autocomplete, no prop validation, no inline documentation.

exports — the modern way to define entry points. Takes precedence over main and module in Node 12+ and modern bundlers. Always define this alongside the legacy fields for maximum compatibility.

files — the whitelist of what gets included in the published package. Only dist goes out. Your src, node_modules, test files, and config files stay local.

sideEffects — tells bundlers which files have side effects and cannot be tree-shaken. Our component files are side-effect-free, but the built output files need to be listed here so styles are not accidentally eliminated.

peerDependencies — declares that React is required but not bundled. Consumers must have React installed themselves. This is what prevents duplicate React instances.


Setting up the .npmignore file

Even with the files field in package.json, an .npmignore file gives you an explicit blocklist as a second layer of protection:

Run npm pack --dry-run at any point to see exactly what would be included in the published package. Check this output before every release — it's the fastest way to catch an accidentally included file.


Setting up your npm account

If you don't have an npm account yet:

If you already have one, log in:

Verify you're logged in:

For a scoped package to be published publicly, you'll need to pass a flag when publishing. We'll cover that below.


Running a test publish with npm pack

Before anything goes live, inspect what you're actually shipping:

This creates a .tgz file locally — the exact archive that would be uploaded to npm. Unzip it and look inside:

You're looking for three things:

  1. The dist/ folder with ESM, CJS, and type declaration files
  2. Nothing from src/ — no raw TypeScript source
  3. No test files, no Storybook files, no config files

If anything unexpected is in there, fix the files field or .npmignore before publishing.


Publishing v0.1.0

Once the build looks right:

The --access public flag is required for scoped packages on the free npm plan. Without it, npm assumes you want to publish privately and will ask you to upgrade.

Go to https://www.npmjs.com/package/@your-username/ui-lib and verify the package is live. You should see the version, the description, and the weekly download count starting at zero.


Installing your own package to verify it works

The final check — install your package into a fresh Vite project and import a component:

Then in src/App.tsx:

If autocomplete works in your editor and the component renders, the publish is correct. If types are missing, the types field in package.json is pointing to the wrong file. If the import fails entirely, the exports field needs debugging.


What not to publish

A few things that trip up first-time library publishers:

Don't publish your src/ folder. Consumers should import from the built output, not your raw TypeScript. Shipping source creates a dependency on your internal structure.

Don't bundle React. We covered this in the Vite config — react and react-dom are external. If they end up in your bundle, you'll get "invalid hook call" errors in consumer apps.

Don't publish a CSS file without documenting it. If your library requires consumers to import a CSS file separately, that needs to be in your README. Otherwise consumers wonder why the styles aren't applying.


Up next: Versioning and changelogs for a UI library — how to do it without embarrassing yourself — semantic versioning in practice, what goes in a changelog, and how to cut a pre-release before you're ready to commit to v1.0.0.

Related: How to structure a UI library that scales — folders, exports, and naming conventions — the folder and export structure in the previous article directly shapes what your built output looks like. Worth reviewing if anything in the dist folder looked unexpected.

Muhammad Athar is the founder of DesignDev.io and the engineer behind everything you read here. He writes about the decisions behind building real products — the components, the architecture, and the tradeoffs that don't make it into the tutorial.

Photo by Compagnons on Unsplash


More Stuff

How to structure a UI library that scales — folders, exports, and naming conventions
April 1, 2026·3 min read

How to structure a UI library that scales — folders, exports, and naming conventions

The folder structure you pick on day one is the one you'll live with across 40 components. Get it wrong and you'll be fighting your own codebase by article 10. Get it right and adding a new component takes five minutes and follows a pattern every contributor can learn in one sitting.

Muhammad Athar

Muhammad Athar

Setting Up a React Component Library with Vite, TypeScript, and Tailwind v4
April 1, 2026·3 min read

Setting Up a React Component Library with Vite, TypeScript, and Tailwind v4

Before you write a single component, the project needs to be set up correctly — or you'll be fighting the build config the entire series. This article covers everything: Vite in library mode, TypeScript strict config, Tailwind v4, CVA, and a working entry point you can build and inspect.

Muhammad Athar

Muhammad Athar

DesignDev.io logoDesignDev.io logo

Explore

  • Home
  • Search
  • Authors
  • Series
  • About Us

Account

  • Sign in

Legal

  • Privacy Policy
  • Terms of Service

© 2026 DesignDev.io · All rights reserved

Built with Next.js · Tailwind · Sanity · Vercel