Skip to content

Node.js npm Package Publishing — Complete Guide to Creating and Distributing Packages

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Node.js npm Package Publishing. We cover key concepts, practical examples, and best practices to help you master this topic.

Node.js npm package publishing involves preparing package.json, configuring access controls, applying semantic versioning, managing .npmignore, and automating releases with CI/CD pipelines.

What You'll Learn

By the end of this tutorial, you'll create and publish npm packages, configure scoped and public packages, automate versioning, set up CI/CD publishing, and follow distribution best practices.

Why Publishing Matters

Publishing packages enables code reuse across projects, contributes to the open source ecosystem, and establishes your expertise. Proper publishing practices protect users from broken releases.

Real-World Use

A team publishes internal utility packages under an @company scope to a private registry, ensuring consistent logging, authentication, and API client patterns across all microservices.

Publishing Path

flowchart LR
  A[Modules] --> B[npm Publishing]
  B --> C[npm Workspaces]
  C --> D[Dependency Management]
  D --> E[Testing]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Package.json Configuration

Essential fields for publishing: name, version, main, files, keywords, license, Repository, and description.

{
  "name": "@username/utility-lib",
  "version": "1.0.0",
  "description": "A collection of Node.js utility functions for string processing and validation",
  "main": "dist/index.js",
  "files": ["dist/", "README.md", "LICENSE"],
  "keywords": ["utilities", "string", "validation"],
  "license": "MIT",
  "repository": { "type": "git", "url": "git+https://github.com/user/repo.git" },
  "engines": { "node": ">=18.0.0" }
}

Semantic Versioning

Follow semver: MAJOR for breaking changes, MINOR for new features, PATCH for bug fixes.

const semver = require("semver");
console.log(semver.valid("1.2.3"));        // 1.2.3
console.log(semver.inc("1.2.3", "major")); // 2.0.0
console.log(semver.inc("1.2.3", "minor")); // 1.3.0
console.log(semver.inc("1.2.3", "patch")); // 1.2.4
console.log(semver.satisfies("1.3.0", "^1.2.0")); // true
console.log(semver.satisfies("2.0.0", "^1.2.0")); // false

Authentication and Publishing

Log in to npm registry and publish the package.

# Login (one time)
npm login --registry=https://registry.npmjs.org

# Publish public package
npm publish

# Publish scoped package as public
npm publish --access public

# Publish a specific tag
npm publish --tag beta

# Publish dry run (no actual publish)
npm publish --dry-run

Using .npmignore

Control which files are included in the published package. Without .npmignore, npm uses .gitignore.

# .npmignore
node_modules/
src/
test/
.git/
.github/
*.log
.DS_Store
tsconfig.json
.eslintrc.js
.prettierrc.js

# The "files" field in package.json overrides .npmignore

Automated CI/CD Publishing

Use GitHub Actions or similar to automate publishing on tags.

# .github/workflows/publish.yml
name: Publish to npm
on:
  release:
    types: [published]
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, registry-url: 'https://registry.npmjs.org' }
      - run: npm ci
      - run: npm run build
      - run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Common Mistakes

1. Publishing Sensitive Information

API keys, passwords, and .env files in published packages expose secrets. Always check files before publishing.

2. Breaking Changes Without Major Version Bump

Breaking changes in minor/patch versions break user applications. Follow semver strictly.

3. Publishing Without Testing

Untested packages damage reputation. Run tests and linting in CI before publishing.

4. Forgetting to Update the Version Locally

Publishing with the same version fails. Use npm version to bump before publish.

5. Not Using .npmignore for Build Artifacts

Source maps, test files, and config files bloat package size. Include only necessary files.

Practice Questions

1. What is the difference between dependencies and devDependencies?

dependencies are required at runtime. devDependencies are needed only during development and testing.

2. How do you publish a scoped package as public?

npm publish --access public. Scoped packages default to private.

3. What is semantic versioning?

MAJOR.MINOR.PATCH: breaking changes (MAJOR), new features (MINOR), bug fixes (PATCH).

4. How does npm decide which files to include in a package?

The files field in package.json, augmented by .npmignore (.gitignore fallback).

5. Challenge: Set up a package that builds TypeScript and publishes only the compiled output.

{
  "scripts": {
    "build": "tsc",
    "prepublishOnly": "npm run build && npm test",
    "version": "git add -A src",
    "postversion": "git push && git push --tags"
  }
}

FAQ

What is the difference between npm publish and npm pack?

npm publish uploads to the registry. npm pack creates a .tgz archive locally for inspection.

Can I unpublish a package?

Yes, within 72 hours. After that, contact npm support. Unpublishing can break other packages.

What are npm dist-tags?

Labels for specific versions. 'latest' is default. Use 'beta', 'next', 'rc' for pre-release versions.

How do I publish to a private registry?

Set registry in .npmrc or use --registry flag. Private registries require authentication.

What is npm provenance?

A security feature that cryptographically links the package to its source repository and CI build.

Mini Project: Package Publishing Script

Build a script that automates version bumping and publishing.

const { execSync } = require("node:child_process");
const semver = require("semver");
const pkg = require("./package.json");
function publish(type) {
  const newVersion = semver.inc(pkg.version, type);
  execSync(`npm version ${newVersion} --no-git-tag-version`, { stdio: "inherit" });
  execSync("npm run build", { stdio: "inherit" });
  execSync("npm test", { stdio: "inherit" });
  execSync(`npm publish --tag ${type === "patch" ? "latest" : type}`, { stdio: "inherit" });
  console.log(`Published version ${newVersion}`);
}
publish(process.argv[2] || "patch");

What's Next

Node.js npm Workspaces Node.js Dependency Management Node.js Testing

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro