npm
07 / 08

Publishing Packages to npm Registry

npm: Publishing Packages

Authentication

# Login to npm registry
npm login                       # prompts for username/password/OTP
npm login --scope=@myorg        # login for org scope

# Check current user
npm whoami

# Logout
npm logout

# Use access token (for CI)
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
# Or set NPM_TOKEN env variable — some registries read it automatically

Package Scope & Access

  • Unscoped packages: published as "my-package", globally unique name

  • Scoped packages: "@myorg/my-package" — org or username prefix, must be published explicitly as public

  • Public packages: anyone can install. Private packages: npm Pro/Teams required (paid).

  • GitHub Packages: alternative registry, free for public packages, integrates with GitHub Actions

Preparing to Publish

# Check what will be included in the published package
npm pack --dry-run
npm pack              # Creates a .tgz file to inspect

# Verify package contents
tar -tzf my-package-1.0.0.tgz | head -50
// Control what's published via "files" field (whitelist)
{
  "files": [
    "dist",
    "README.md",
    "!dist/**/*.test.*",
    "!dist/**/*.map"
  ]
}

// Always excluded: .git, node_modules, .npmrc, .env, *.log
// Always included: package.json, README.md, LICENSE, CHANGELOG.md

Publishing

# Publish for the first time
npm publish                              # defaults to latest tag
npm publish --access public              # required for scoped packages

# Publish with a tag (won't be installed by default with @latest)
npm publish --tag beta
npm publish --tag next

# Install specific tag
npm install my-package@beta
npm install my-package@next

Versioning Workflow

# Bump version (updates package.json, creates git tag)
npm version patch    # 1.0.0 → 1.0.1
npm version minor    # 1.0.0 → 1.1.0
npm version major    # 1.0.0 → 2.0.0

npm version 1.2.3    # set exact version
npm version prerelease --preid beta   # 1.0.0 → 1.0.1-beta.0

# Typical release workflow
npm run check        # lint + test + typecheck
npm run build        # compile
npm version patch    # bump version, creates git tag
git push && git push --tags
npm publish

# Or use release tools
npx release-it       # interactive release workflow
npx changeset        # monorepo-friendly changelog management

dist-tags

# Manage distribution tags
npm dist-tag ls my-package          # show all tags
npm dist-tag add my-package@1.0.0 latest
npm dist-tag add my-package@2.0.0-beta.1 beta
npm dist-tag rm my-package old-tag

# Users install 'latest' by default:
npm install my-package       # installs 'latest' tag
npm install my-package@beta  # installs 'beta' tag

Package Deprecation

# Deprecate a version (warning shown on install)
npm deprecate my-package@"< 2.0.0" "Please upgrade to v2.0.0+"

# Deprecate all versions
npm deprecate my-package "This package is no longer maintained"

# Unpublish (only within 72 hours of publish, strict policy)
npm unpublish my-package@1.0.0    # specific version
# After 72h: contact npm support
# Better alternative: deprecate instead of unpublish

GitHub Packages Registry

# .npmrc to use GitHub registry for a scope
@myorg:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

# Publish to GitHub Packages
npm publish --registry https://npm.pkg.github.com

# package.json name must match GitHub org
# "@myorg/my-package" → github.com/myorg/my-package

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free