Skip to Content
Build and Publish a Small npm Package
Advanced
0 likes
Tutorials

Build and Publish a Small npm Package

Set up name, version, exports and files in package.json, check exactly what ships with npm pack --dry-run, then publish with npm publish. Mistakes in a published version can't be undone.

npm publish uploads your package, and that version is permanent. You can deprecate it. Unpublishing is limited (freely only in the first 72 hours, and not once other packages depend on it), and the version number can never be reused. So publishing is the last step, after you've checked exactly which files go out and tested the package the way a user would install it.

The example is a small library, @xtensyon/peso, that formats centavo amounts as pesos. It was built and checked with every command below, and never actually published. npm publish --dry-run stands in for the real publish.

Step 1: the package.json fields

{
  "name": "@xtensyon/peso",
  "version": "0.1.0",
  "description": "Format and add Philippine peso amounts stored as centavos.",
  "type": "module",
  "files": ["dist"],
  "main": "./dist/peso.cjs",
  "module": "./dist/peso.js",
  "exports": {
    ".": {
      "import": "./dist/peso.js",
      "require": "./dist/peso.cjs"
    }
  },
  "scripts": {
    "build": "vite build",
    "prepublishOnly": "npm run build"
  },
  "license": "MIT"
}
  • files lists what goes into the package. Everything else stays home.
  • exports lists what people can import. Internal files stay internal.
  • prepublishOnly builds right before publishing, so you never ship an old dist.
  • Pick a license. Without one, people legally can't use your code.

The build here is Vite's library mode, which writes dist/peso.js and dist/peso.cjs. Any build tool works. The checks below don't change.

Step 2: see what would ship

npm pack --dry-run
npm notice 📦  @xtensyon/peso@0.1.0
npm notice Tarball Contents
npm notice 17B README.md
npm notice 262B dist/peso.cjs
npm notice 265B dist/peso.js
npm notice 500B package.json
...
npm notice total files: 4

Four files, all expected. For comparison, the same project without the files line:

npm notice 22B .env
npm notice 17B README.md
npm notice 262B dist/peso.cjs
npm notice 265B dist/peso.js
npm notice 470B package.json
npm notice 341B src/index.js
npm notice 209B vite.config.js
npm notice 715B xtensyon-peso-0.1.0.tgz
npm notice total files: 8

The .env file is in there. npm doesn't exclude .env on its own. Read this list every time, not just the first time.

âš  Warning: If a secret was ever in a published package, treat it as leaked. Revoke it and make a new one. Unpublishing doesn't pull it back from caches and mirrors.

Step 3: install the real tarball somewhere else

npm pack
mkdir ../try-peso && cd ../try-peso
npm init -y
npm install ../peso/xtensyon-peso-0.1.0.tgz
node --input-type=module -e 'import { formatPeso, sumCentavos } from "@xtensyon/peso"; console.log(formatPeso(sumCentavos([45000, 120050])));'
node -e 'const { formatPeso } = require("@xtensyon/peso"); console.log(formatPeso(99));'
₱1,650.50
₱0.99

This tests what users actually get: the packed files, resolved through exports. A typo in an exports path or a file missing from files fails here instead of in someone else's project.

(On Windows PowerShell, swap the single and double quotes in the -e lines.)

Step 4: a dry run of the publish

npm publish --dry-run
npm notice Publishing to https://registry.npmjs.org/ with tag latest and default access (dry-run)
+ @xtensyon/peso@0.1.0

It runs everything except the upload, including prepublishOnly. If you're not logged in, it warns about that too.

Step 5: publish for real

When you're ready, and only then:

npm login
npm publish --access public

Scoped packages (@name/...) are restricted by default, so the first publish needs --access public. Turn on two-factor authentication on your npm account before your first publish. To publish from CI later, use npm's trusted publishing or a token limited to this one package.

Every release after that

  1. Bump the version: npm version patch (bug fix), minor (new feature), major (breaking change). It also creates a git commit and tag.
  2. npm pack --dry-run and read the list.
  3. npm publish.
  4. git push --follow-tags.

💡 Tip: Made a mistake in a published version? Publish a fixed version with a higher number, then npm deprecate @xtensyon/peso@0.1.1 "Broken export, use 0.1.2". Users get a warning on install instead of a broken package.

Run npm pack --dry-run on the last project you worked on. Which files would you have shipped by accident?

About

Prepare package.json, build, inspect the tarball with npm pack, test it in another project, and only then publish. After this, your first publish doesn't ship your .env file.

Responsible
Jay P
Last Update
09/08/2026
Reading Time
4 min
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.