npm 18 ๐ Publishing Public and Private Packages (npm publish, –access public)
Publishing a package to the npm registry is a single command, but the result depends on a decision made before the command runs: is the package scoped or unscoped, and is the account paid or free? The npm publish command reads the package name in package.json and applies a default access level based on whether the name contains a scope. Unscoped packages are always public. Scoped packages default to restricted, which means they require a paid account unless the --access public flag is passed . The flag is not a preference; it is the mechanism that determines whether the publish succeeds on a free account and whether the package is visible to the world.
Key point: A scoped package published without --access public is private by default. On a free account, that publish fails with an HTTP 402 error unless the flag is present. The flag is required for the first publish; subsequent access changes can be made with npm access set status .
Why the access level is a publishing decision
The scope-as-namespace problem. A scope (@username/package or @org/package) groups related packages under a namespace. It prevents name collisions and allows an organization to control who can publish within the namespace . The scope is not just a naming convention; it is the unit of access control. A scoped package can be public or private, and the default depends on the account type and the publish command.
The free-tier problem. Private packages require a paid npm account or a paid organization . A free account can publish scoped packages, but only as public. The --access public flag is what tells the registry to treat the scoped package as public rather than restricted. Without it, the registry attempts to create a private package, and the free account is rejected with a 402 status . This is the most common first-publish failure for developers who use a scope because their username is already taken as an unscoped name.
The accidental-private problem. A developer who publishes a scoped package without the flag on a paid account succeeds, but the package is private. It is visible only to the owner and to users granted read access. The npm website shows “private” below the package name . If the intent was a public package, the publish succeeded but the outcome was wrong. The flag must be present on the first publish, or the access must be changed afterward with npm access set status=public .
a. Publishing an Unscoped Public Package
An unscoped package has a name without an @scope/ prefix. It is always public, and no access flag is required. The publish command is simply npm publish from the package root .
# Navigate to the package root
cd /path/to/package
# Publish the unscoped package
npm publish
The package is published to the public registry, and the npm website displays it at https://npmjs.com/package/<package-name>. The website shows “public” below the package name . No --access flag is needed because the unscoped namespace has no private variant.
The prerequisites are the same as any publish: a verified email, 2FA enabled or a granular access token with 2FA bypass, and a unique name and version combination . The command fails if the name and version already exist, and the specific combination can never be reused even after npm unpublish .
b. Publishing a Scoped Package as Public
A scoped package has a name in the form @scope/package. The scope is either the username or an organization name. The default access for a scoped package is restricted, which means private. To publish it as public, the --access public flag must be passed on the first publish .
# Publish a scoped package as public
npm publish --access public
If the account is free, this flag is mandatory. The registry rejects the publish with a 402 status if the flag is absent, because the free account cannot create private packages . If the account is paid, the flag is still necessary to make the package public; without it, the publish succeeds but the package is private.
The publishConfig field in package.json provides an alternative to the command-line flag. Setting "publishConfig": { "access": "public" } makes the default access public for that package, so npm publish without the flag produces the same result . This is useful for automation, where the flag might be forgotten. The field has no effect on unscoped packages and should not be set for them.
c. Publishing a Private Package
A private package is visible only to the owner and to users granted read access. It requires a paid account for user-scoped packages or a paid organization for org-scoped packages . The publish command is npm publish without the --access public flag, because restricted is the default for scoped packages.
# Publish a private scoped package (paid account required)
npm publish
The npm website shows “private” below the package name. The package is installable by the owner and by users who have been granted read access through npm access grant . The npm access command manages these grants: npm access grant read-only @scope:team package-name gives a team read access, and npm access revoke removes it.
A package with "private": true in package.json is a different concept. That field prevents the package from being published at all, regardless of the access flag. It is used for workspace packages that are not intended for the registry . A private package that is meant to be published has no "private": true field; it is private because of its access level, not because of a flag in the manifest.
Complete Example Session
# ============================================
# PART 1: CHECK THE PACKAGE NAME
# ============================================
# The name determines the default access
grep '"name"' package.json
# "name": "my-package" โ unscoped, always public
# "name": "@user/my-package" โ scoped, defaults to restricted
# ============================================
# PART 2: PUBLISH AN UNSCOPED PACKAGE
# ============================================
# No access flag needed
npm publish
# Published as public
# ============================================
# PART 3: PUBLISH A SCOPED PACKAGE AS PUBLIC (FREE ACCOUNT)
# ============================================
# The flag is required on a free account
npm publish --access public
# Published as public
# ============================================
# PART 4: PUBLISH A SCOPED PACKAGE AS PUBLIC (PAID ACCOUNT)
# ============================================
# The flag is still required for public visibility
npm publish --access public
# Without the flag, the package would be private
# ============================================
# PART 5: PUBLISH A SCOPED PACKAGE AS PRIVATE
# ============================================
# Paid account or organization required
npm publish
# Published as restricted
# ============================================
# PART 6: USE publishConfig INSTEAD OF THE FLAG
# ============================================
# package.json
{
"publishConfig": {
"access": "public"
}
}
# npm publish now defaults to public
# ============================================
# PART 7: CHANGE ACCESS AFTER PUBLISH
# ============================================
# Change a private package to public
npm access set status=public my-package
# Change a public package to private
npm access set status=private my-package
# ============================================
# PART 8: GRANT READ ACCESS TO A PRIVATE PACKAGE
# ============================================
npm access grant read-only @my-org:developers my-package
# ============================================
# PART 9: VERIFY THE PUBLISH
# ============================================
# Visit the package page
# https://npmjs.com/package/my-package
# "public" or "private" appears below the name
# ============================================
# PART 10: COMMON FAILURE
# ============================================
# Free account, scoped package, no flag:
npm publish
# Error: HTTP 402 Payment Required
# Fix: npm publish --access public
The ten parts trace the decision from the package name through the access flag to the post-publish verification. Part 5 shows the private publish that requires a paid account. Part 10 shows the failure mode that the flag prevents.
Quick Reference
Access by Package Type
| Package Name | Default Access | Flag Required (Free) |
|---|---|---|
my-package (unscoped) | Public | No |
@user/my-package (scoped) | Restricted | Yes, for public |
@org/my-package (scoped) | Restricted | Yes, for public |
Publish Commands
| Command | Result |
|---|---|
npm publish (unscoped) | Public |
npm publish --access public (scoped) | Public |
npm publish (scoped, paid) | Private |
npm publish (scoped, free) | 402 error |
Access Management
| Command | Purpose |
|---|---|
npm access set status=public | Make a package public |
npm access set status=private | Make a package private |
npm access grant read-only | Grant read access to a team |
npm access revoke | Remove access |
Best Practices
โ Do This:
# Use --access public for scoped public packages
npm publish --access public # โ
// Set publishConfig for automation
"publishConfig": { "access": "public" } // โ
# Verify the access level on the npm website after publish
# "public" or "private" appears below the package name # โ
# Use npm access to change access after the initial publish
npm access set status=public my-package # โ
โ Don’t Do This:
# Publish a scoped package without the flag on a free account
npm publish # fails with 402 # โ
# Assume a scoped package is public by default
# It is restricted by default # โ
# Use "private": true for a package meant to be published
# It prevents publishing entirely # โ
# Forget that access can only be changed by the owner or a team member
# The grant must exist before the change # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| 402 error on scoped publish | Free account, no --access public | Add the flag |
| Scoped package is private | Default access is restricted | npm access set status=public or republish with flag |
"private": true prevents publish | The field blocks all publishing | Remove the field |
| Access change fails | User is not an owner | Verify ownership with npm access list packages |
Real-World Examples
1. Unscoped Public Publish
npm publish
2. Scoped Public Publish
npm publish --access public
3. Scoped Private Publish
npm publish
4. publishConfig Alternative
"publishConfig": { "access": "public" }
5. Change to Public
npm access set status=public my-package
6. Change to Private
npm access set status=private my-package
7. Grant Read Access
npm access grant read-only @org:team my-package
8. Revoke Access
npm access revoke @org:team my-package
9. List Package Access
npm access list packages
10. Check Collaborators
npm access list collaborators my-package
Visual
The Access Decision Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PACKAGE NAME โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ Unscoped โ โ Scoped โ โ
โ โ my-package โ โ @user/my-packageโ โ
โ โโโโโโโโโโฌโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ โ
โ โผ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ Always public โ โ Default: โ โ
โ โ No flag needed โ โ restricted โ โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโดโโโโโโโโโโโโ โ
โ โผ โผ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ --access โ โ No flag โ โ
โ โ public โ โ โ โ
โ โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โ
โ โ โ โ
โ โผ โผ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ Public โ โ Private โ โ
โ โ (free OK) โ โ (paid only) โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ
โ The flag is the difference between a free public scoped โ
โ package and a 402 error. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The 402 Failure
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FREE ACCOUNT โ
โ โ
โ npm publish โ
โ (scoped package, no --access public) โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Registry tries โ โ
โ โ to create โ โ
โ โ private package โ โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ HTTP 402 โ Payment Required โ
โ โ Payment Requiredโ โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Fix: add โ โ
โ โ --access public โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ The flag tells the registry to create a public package, โ
โ which the free account is allowed to do. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Access Management After Publish
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PUBLISHED PACKAGE โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ Current access โ โ
โ โ (public or โ โ
โ โ private) โ โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ npm access set โ Change status โ
โ โ status=public โ โ
โ โ or private โ โ
โ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ npm access โ Grant/revoke team access โ
โ โ grant/revoke โ โ
โ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Access can be changed after publish, but the initial โ
โ publish determines whether the package exists at all on โ
โ a free account. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Scope and Visibility Matrix
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SCOPE ACCESS WHO CAN VIEW โ
โ โ
โ Unscoped Public Everyone โ
โ โ
โ User scoped Public Everyone โ
โ User scoped Private Owner + granted users โ
โ โ
โ Org scoped Public Everyone โ
โ Org scoped Private Org team members with read access โ
โ โ
โ Only user accounts can create unscoped packages. โ
โ Organizations can only manage scoped packages. โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Unscoped package | Always public; no flag required |
| Scoped package default | Restricted (private) |
| Public scoped flag | --access public |
| Free account + scoped + no flag | HTTP 402 error |
publishConfig | "access": "public" in package.json |
| Change access after publish | npm access set status=public|private |
| Grant read access | npm access grant read-only |
"private": true | Prevents publishing entirely |
Key takeaways:
- The scope determines the default access. An unscoped package is always public. A scoped package is restricted by default, regardless of account type. The
--access publicflag is what makes a scoped package public, and on a free account it is mandatory for any scoped publish . - The 402 error is a missing flag, not a missing account. A free account can publish scoped packages, but only as public. The registry rejects the publish when the default (restricted) would require a paid plan. Adding
--access publicresolves the error without any account change. publishConfigis the automation-friendly alternative. Setting"access": "public"inpackage.jsonmakes the default public for that package, sonpm publishwithout the flag produces the intended result. This is useful in CI pipelines where the flag might be omitted .- Private packages require a paid account. A scoped package published without
--access publicon a paid account succeeds as private. The npm website shows “private” below the name, and only the owner and granted users can install it . "private": trueis not the same as private access. That field prevents the package from being published at all. It is for workspace packages that should never reach the registry. A private package that is meant to be published has no"private": truefield; its privacy comes from its access level .
Remember: The npm publish command does not ask whether the package should be public or private. It applies a default based on the name and the account, and the result may not match the intent. The scope is the signal that the package belongs to a namespace, and the default for that namespace is restricted. The --access public flag is the override that makes the package visible to everyone and makes the publish succeed on a free account. The flag is not a workaround; it is the correct syntax for the intended outcome. When the intent is private, the flag is absent, and the account must be paid. When the intent is public, the flag is present, and the account type does not matter.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!