| |

Yarn 13 ๐Ÿงถ Publishing Packages to Registries

โฑ๏ธ Estimated read: 8 min | |

Publishing a package is the act of making it available for others to install. In Yarn Berry, the command is yarn npm publish. It packs the active workspace into a tarball and uploads it to a registry. The critical difference from Yarn Classic is the command name: Classic used yarn publish, Berry uses yarn npm publish. The second critical difference is the registry configuration: Berry does not read .npmrc or the --registry flag. It resolves the publish destination from publishConfig.registry in package.json, then npmPublishRegistry in .yarnrc.yml, then the default npm registry .

Key point: A package must have a name and version, and must not be private: true. Scoped packages default to restricted access (paid). Use --access public for free public scoped packages.


Why yarn npm publish exists

The Classic-to-Berry migration problem. Yarn Classic’s yarn publish is gone. Any automation, CI script, or documentation that references it will fail on Berry. The replacement is yarn npm publish, which operates on the active workspace rather than the current directory .

The scoped package access problem. By default, a scoped package (@myorg/utils) is published as restricted, which requires a paid npm plan. This is a legacy behavior that surprises developers. The fix is the --access public flag, or setting npmPublishAccess: public globally in .yarnrc.yml .

The registry routing problem. Berry does not recognize .npmrc for Yarn-specific configuration. Private registries must be configured via npmScopes in .yarnrc.yml, with npmPublishRegistry and npmAuthToken. The token should be injected from an environment variable, never hardcoded .

The staged publishing problem. Berry supports --staged, which uploads the package but does not publish it until a separate approval step. This is useful for organizations that require a human review before a package goes live. The staged package is managed with yarn npm stage list, yarn npm stage approve, and yarn npm stage reject .

The provenance problem. The --provenance flag generates a signed attestation linking the package to the source repository and build. It is only available in GitHub Actions and GitLab CI, and it provides a verifiable chain of custody from source to registry .


a. Basic publishing workflow

Before publishing, the package must be valid. The name and version fields are mandatory. The private field must be absent or false. The version must follow Semantic Versioning .

# Verify the package is not private
grep '"private"' package.json
# If present and true, remove it.

# Dry run to see what would be published
yarn npm publish --dry-run

# Publish to the default registry
yarn npm publish

The --dry-run flag prints the tarball contents and metadata without uploading. This catches missing files, incorrect entry points, and accidental inclusion of secrets before the package is live .

For a public scoped package, the --access public flag is required:

yarn npm publish --access public

Without it, Yarn attempts to publish as restricted, which fails if the account does not have a paid plan .


b. Configuring private registries

Private registries are configured in .yarnrc.yml under npmScopes. The scope name excludes the @ symbol. The npmPublishRegistry is the URL where packages are uploaded. The npmAlwaysAuth flag sends the token on every request. The npmAuthToken should reference an environment variable .

npmScopes:
  my-company:
    npmPublishRegistry: "https://npm.internal.example.com"
    npmAlwaysAuth: true
    npmAuthToken: "${INTERNAL_NPM_TOKEN}"

With this configuration, yarn npm publish from a workspace named @my-company/utils uploads to the internal registry. No other flags are needed .

The publishConfig field in package.json can override the registry for a specific package. This is useful when a monorepo contains packages that publish to different registries .

{
  "publishConfig": {
    "registry": "https://npm.internal.example.com"
  }
}

c. Staged publishing and provenance

The --staged flag uploads the package without publishing it immediately. The package is held in a staging area. A separate approval step is required before it becomes visible to installers .

# Stage the package
yarn npm publish --staged

# List staged packages
yarn npm stage list

# Approve a staged package
yarn npm stage approve <package-name>@<version>

# Reject a staged package
yarn npm stage reject <package-name>@<version>

The --provenance flag generates a provenance statement. This is a signed attestation that records the source repository, the commit, the build instructions, and the resulting tarball hash. It is available only in CI environments (GitHub Actions, GitLab CI) where the identity of the builder can be verified .

yarn npm publish --provenance

The provenance is uploaded alongside the package and is visible on the registry’s package page. It allows consumers to verify that the package they downloaded was built from the source they expect.


Complete Example Session

# ============================================
# PART 1: PREPARE THE PACKAGE
# ============================================

# Ensure name and version are set
grep -E '"(name|version)"' package.json
# "name": "@myorg/utils"
# "version": "1.2.0"

# Ensure private is not true
grep '"private"' package.json
# (no output means not private)
# ============================================
# PART 2: DRY RUN
# ============================================

yarn npm publish --dry-run
# Lists files that would be included.
# Verify no secrets, no node_modules, no test files.
# ============================================
# PART 3: CONFIGURE PRIVATE REGISTRY
# ============================================

# .yarnrc.yml
npmScopes:
  myorg:
    npmPublishRegistry: "https://npm.internal.example.com"
    npmAlwaysAuth: true
    npmAuthToken: "${NPM_TOKEN}"
# ============================================
# PART 4: PUBLISH PUBLIC SCOPED PACKAGE
# ============================================

yarn npm publish --access public
# Package uploaded to the configured registry.
# ============================================
# PART 5: PUBLISH WITH A TAG
# ============================================

yarn npm publish --tag beta
# Package attached to the "beta" dist-tag.
# Install with: yarn add @myorg/utils@beta
# ============================================
# PART 6: STAGED PUBLISHING
# ============================================

yarn npm publish --staged
# Package uploaded but not visible to installers.
# ============================================
# PART 7: APPROVE STAGED PACKAGE
# ============================================

yarn npm stage list
# Lists pending packages.
yarn npm stage approve @myorg/utils@1.2.0
# Package is now live.
# ============================================
# PART 8: PROVENANCE (CI ONLY)
# ============================================

# In GitHub Actions:
yarn npm publish --provenance
# Generates signed attestation linking package to source.

The eight parts trace the publishing lifecycle: preparation, dry run, registry configuration, public access, tagging, staging, approval, and provenance.


Quick Reference

Publish Commands

CommandPurpose
yarn npm publishPublish active workspace
yarn npm publish --dry-runPreview without uploading
yarn npm publish --access publicPublic scoped package
yarn npm publish --tag betaAttach to dist-tag
yarn npm publish --stagedStage for approval
yarn npm publish --provenanceGenerate attestation

Registry Configuration

SettingLocation
npmPublishRegistry.yarnrc.yml under npmScopes
npmAuthToken.yarnrc.yml (use ${VAR})
publishConfig.registrypackage.json (overrides)
npmPublishAccess.yarnrc.yml (default access)

Staged Publishing Commands

CommandPurpose
yarn npm stage listList staged packages
yarn npm stage approvePublish a staged package
yarn npm stage rejectDiscard a staged package

Best Practices

โœ… Do This:

yarn npm publish --dry-run
<!-- Always preview before publishing -->            # โœ…
npmAuthToken: "${NPM_TOKEN}"
<!-- Inject token from environment -->              # โœ…
yarn npm publish --access public
<!-- Use --access public for free scoped packages --> # โœ…

โŒ Don’t Do This:

yarn publish
<!-- Classic command; fails on Yarn Berry -->       # โŒ
# Hardcode npmAuthToken in .yarnrc.yml
<!-- Token leaks into version control -->           # โŒ
# Publish without --dry-run
<!-- Missing files discovered after publishing -->   # โŒ

Common Pitfalls

PitfallWhy It HappensFix
yarn publish not foundBerry uses yarn npm publishUse the correct command
Scoped package publish failsDefault access is restrictedAdd --access public
Private registry not usedNo npmScopes configurationConfigure in .yarnrc.yml
Version already existsSame version published previouslyBump version or use --tolerate-republish
Dry run shows secrets.gitignore not respectedVerify files field in package.json

Real-World Examples

1. Basic Publish

yarn npm publish

2. Public Scoped Package

yarn npm publish --access public

3. Dry Run

yarn npm publish --dry-run

4. Private Registry

npmScopes:
  myorg:
    npmPublishRegistry: "https://npm.internal.example.com"

5. Token from Environment

npmAuthToken: "${NPM_TOKEN}"

6. Publish with Tag

yarn npm publish --tag next

7. Staged Publish

yarn npm publish --staged

8. Approve Staged

yarn npm stage approve @myorg/pkg@1.0.0

9. Provenance

yarn npm publish --provenance

10. CI Configuration

# .github/workflows/publish.yml
run: yarn npm publish --access public
env:
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Visual

Publish Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  yarn npm publish                            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Pack active workspace โ†’ tarball             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Resolve registry:                           โ”‚
โ”‚  1. publishConfig.registry                   โ”‚
โ”‚  2. npmPublishRegistry (npmScopes)           โ”‚
โ”‚  3. Default npm registry                     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Upload tarball + metadata                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Package available for install               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Staged vs Direct Publish

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  yarn npm publish                            โ”‚
โ”‚  โ†’ Package immediately visible               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  yarn npm publish --staged                   โ”‚
โ”‚  โ†’ Package uploaded but hidden               โ”‚
โ”‚  โ†’ yarn npm stage approve โ†’ visible          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Staging adds an approval gate before visibility.


Summary

ItemValue
Publish commandyarn npm publish
Classic equivalentyarn publish (not supported)
Scoped package defaultrestricted (paid)
Public scoped flag--access public
Private registry confignpmScopes in .yarnrc.yml
Token injection${ENV_VAR}
Dry run--dry-run
Staged publish--staged
Provenance--provenance (CI only)

Key takeaways:

  • The command is yarn npm publish, not yarn publish. Berry’s command operates on the active workspace. Classic’s command is gone .
  • Scoped packages default to restricted. This requires a paid npm plan. Use --access public for free public scoped packages .
  • Private registries are configured in .yarnrc.yml. The npmScopes section maps scopes to registry URLs and tokens. .npmrc is not read for Yarn-specific configuration .
  • Always dry run first. --dry-run shows the exact tarball contents without uploading, catching missing files and accidental secrets .
  • Staged publishing adds a review gate. --staged uploads without publishing, and a separate approval step makes the package visible .

Remember: Publishing is irreversible. A published version cannot be modified or deleted from the registry (in most cases). The dry run is not optional. The token should never be committed. The registry configuration belongs in .yarnrc.yml, not in shell commands. And the command is yarn npm publish, not the Classic yarn publish.

Was this helpful?


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!