pnpm 12 📦 Patches and Overrides: Customizing Third-Party Dependencies
A dependency has a bug. The upstream fix is months away, or the maintainer is unresponsive, or the fix exists only in a pull request that has not been merged. Forking the repository is heavy: you lose the link to the original, you must maintain the fork, and your package.json points to a Git URL that may break. pnpm provides two mechanisms for this problem without forking: patches modify the source code of a package, and overrides force a specific version of a transitive dependency. The distinction matters for the exam. A patch changes what a package does. An override changes which package is used.
Key point: pnpm patch <pkg>@<version> extracts the package into a temporary directory. pnpm patch-commit <path> generates a patch file and registers it in pnpm-workspace.yaml under patchedDependencies. Overrides are declared in the same file under overrides. Patches are for code changes; overrides are for version pinning.
Why patches and overrides exist
The upstream lag problem. A critical bug blocks your application. The fix is a one-line change, but the package maintainer has not released a new version. The pnpm documentation recommends making a pull request upstream, but acknowledges that review and release can take time. In the meantime, pnpm patch lets you unblock yourself without waiting .
The dependency pinning problem. A transitive dependency has a vulnerability or a breaking change. You cannot edit the intermediate package that declares the dependency, and you cannot wait for it to release a new version. overrides forces the entire dependency graph to use a specific version of the transitive package, regardless of what the intermediate package declares .
The patch versus override distinction. A patch modifies the source code of a package. An override changes the version resolution. If a package imports a dependency that does not exist, a patch that adds the import will fail because the dependency is not installed. The correct fix is an override (to install the missing dependency) followed by a patch (to add the import). The pnpm documentation explicitly warns: “To change a package’s dependencies, don’t use pnpm patch to modify its package.json! Use overrides or a package hook instead” .
The workspace sharing problem. Patches are stored in a patches/ directory at the workspace root and registered in pnpm-workspace.yaml. Every workspace package that uses the patched version receives the patch. This is the correct behavior: a patch is a project-wide decision, not a per-package one .
The trade-off. A patch is a divergence from the published package. It must be documented, reviewed, and removed when the upstream fix lands. An override is a version constraint that may conflict with the expectations of the intermediate package. Both are temporary measures, not permanent solutions.
a. Creating and applying patches
The patch workflow begins with pnpm patch <package>@<version>. The version must be specified. pnpm extracts the package into a temporary directory and prints the path.
# Extract the package for editing
pnpm patch lodash@4.17.21
# Output: You can now edit the following folder: /tmp/pnpm-patch-abc123
The extracted folder contains the exact files that pnpm would have installed. You can edit any file in that directory: JavaScript source, TypeScript declarations, or configuration files. The changes are isolated to the temporary copy.
After editing, pnpm patch-commit <path> generates the patch file and registers it. The patch is written to the patches/ directory at the workspace root.
# Generate and register the patch
pnpm patch-commit /tmp/pnpm-patch-abc123
The pnpm-workspace.yaml file is updated with a patchedDependencies entry:
patchedDependencies:
lodash@4.17.21: patches/lodash@4.17.21.patch
The patch file is a standard unified diff. It is committed to Git alongside pnpm-workspace.yaml and pnpm-lock.yaml. On every subsequent pnpm install, pnpm applies the patch automatically .
b. Patch version ranges and failure handling
A patch can be applied to an exact version, a version range, or a package name without a version. The priority order is: exact version (highest), version range, name-only (applies to all versions unless overridden) .
patchedDependencies:
foo: patches/foo-1.patch
foo@^2.0.0: patches/foo-2.patch
foo@2.1.0: patches/foo-3.patch
In this example, foo@2.1.0 receives patches/foo-3.patch. All other versions matching ^2.0.0 receive patches/foo-2.patch. All remaining versions receive patches/foo-1.patch. Overlapping version ranges should be avoided; if a sub-range needs a specialized patch, exclude it explicitly from the broader range .
The allowUnusedPatches setting controls whether the install fails when a registered patch is not applied. The default is false, which means an unused patch is an error. Setting it to true suppresses the error, which is useful when a patch is conditional on a version that may or may not be installed .
In pnpm 11, ignorePatchFailures was removed. A patch that fails to apply always throws an error. When multiple patches are applied, all errors are reported together at the end .
c. Overrides: forcing transitive dependency versions
Overrides are declared in pnpm-workspace.yaml under the overrides key. The syntax is a map of package names to versions.
overrides:
lodash: 4.17.21
"foo@^1.0.0": 1.2.3
An override without a version range applies to every instance of the package in the graph. An override with a range applies only to versions that match the range. The pnpm why <package> command shows the resolution chain and confirms whether the override was applied .
Overrides are different from patches in a critical way. A patch modifies a package that is already installed. An override changes which package version is installed. If a transitive dependency is vulnerable, the override forces a patched version without modifying any source code.
The pnpm.overrides field in package.json was the pnpm ≤10 location. In pnpm 11, the setting moved to pnpm-workspace.yaml. npm’s overrides field in package.json is also read for compatibility, but the canonical location for pnpm-specific configuration is the YAML file .
Complete Example Session
# ============================================
# PART 1: EXTRACT A PACKAGE FOR PATCHING
# ============================================
pnpm patch lodash@4.17.21
# Output: You can now edit the following folder:
# /tmp/pnpm-patch-abc123
# ============================================
# PART 2: EDIT THE EXTRACTED FILES
# ============================================
cd /tmp/pnpm-patch-abc123
# Modify lodash.js or any other file.
# For example, change a default timeout value.
# ============================================
# PART 3: COMMIT THE PATCH
# ============================================
pnpm patch-commit /tmp/pnpm-patch-abc123
# Patch written to patches/lodash@4.17.21.patch
# pnpm-workspace.yaml updated with patchedDependencies.
# ============================================
# PART 4: VERIFY THE PATCH REGISTRATION
# ============================================
grep -A 2 patchedDependencies pnpm-workspace.yaml
# patchedDependencies:
# lodash@4.17.21: patches/lodash@4.17.21.patch
# ============================================
# PART 5: REINSTALL TO APPLY
# ============================================
pnpm install
# The patch is applied during installation.
# ============================================
# PART 6: ADD AN OVERRIDE FOR A TRANSITIVE DEPENDENCY
# ============================================
cat >> pnpm-workspace.yaml <<'EOF'
overrides:
"minimist@<1.2.6": 1.2.6
EOF
# ============================================
# PART 7: VERIFY THE OVERRIDE
# ============================================
pnpm why minimist
# Shows minimist@1.2.6 in the resolution chain.
# ============================================
# PART 8: REMOVE A PATCH
# ============================================
pnpm patch-remove lodash@4.17.21
# Removes the patch file and the patchedDependencies entry.
pnpm install
# ============================================
# PART 9: HANDLE UNUSED PATCHES
# ============================================
# In pnpm-workspace.yaml:
allowUnusedPatches: true
# Installation will not fail if a patch is not applied.
The nine parts trace the lifecycle: extraction, editing, committing, verification, installation, override addition, override verification, patch removal, and unused patch handling.
Quick Reference
Patch Commands
| Command | Purpose |
|---|---|
pnpm patch <pkg>@<version> | Extract package for editing |
pnpm patch-commit <path> | Generate patch and register |
pnpm patch-remove <pkg>@<version> | Remove a patch |
Patch Configuration
| Setting | Location | Purpose |
|---|---|---|
patchedDependencies | pnpm-workspace.yaml | Map of package to patch file |
allowUnusedPatches | pnpm-workspace.yaml | Suppress error for unused patches |
Override Configuration
| Setting | Location | Purpose |
|---|---|---|
overrides | pnpm-workspace.yaml | Force dependency versions |
pnpm.overrides | package.json (≤10) | Legacy location |
Patch Priority
| Key Type | Priority |
|---|---|
Exact version (foo@2.1.0) | Highest |
Version range (foo@^2.0.0) | Medium |
Name only (foo) | Lowest |
Best Practices
✅ Do This:
pnpm patch lodash@4.17.21
<!-- Specify the exact version to patch --> # ✅
overrides:
"minimist@<1.2.6": 1.2.6
<!-- Pin vulnerable transitive dependency --> # ✅
# Document patches in patches/README.md
<!-- Explain why the patch exists --> # ✅
❌ Don’t Do This:
# Use pnpm patch to modify package.json dependencies
<!-- Use overrides or packageExtensions instead --> # ❌
# Assume a patch survives a version upgrade
<!-- Patches are version-specific --> # ❌
# Enable allowUnusedPatches without reviewing
<!-- May hide a patch that failed to apply --> # ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Patch not applied | Version mismatch | Use exact version or update patch |
| Patch rejected | Syntax error in diff | Regenerate with pnpm patch |
| Override not effective | Wrong range syntax | Verify with pnpm why |
| Patch file missing from Git | patches/ not committed | git add patches/ |
| Unused patch error | Package version changed | Update patch or set allowUnusedPatches |
Real-World Examples
1. Extract for Patching
pnpm patch express@4.18.2
2. Commit Patch
pnpm patch-commit /tmp/pnpm-patch-abc123
3. Patch Registration
patchedDependencies:
express@4.18.2: patches/express@4.18.2.patch
4. Override Transitive Version
overrides:
lodash: 4.17.21
5. Override by Range
overrides:
"foo@^1.0.0": 1.2.3
6. Verify Override
pnpm why minimist
7. Remove Patch
pnpm patch-remove express@4.18.2
8. Allow Unused Patches
allowUnusedPatches: true
9. Check Patched Packages
pnpm list --depth=0 | grep patched
10. Document Patch
# patches/README.md
## express@4.18.2.patch
Fixes timeout. Upstream PR: #1234
Visual
Patch vs Override
┌──────────────────────────────────────────────┐
│ PATCH │
│ ├── Modifies package source code │
│ ├── Stored as unified diff │
│ ├── Version-specific │
│ └── Registered in patchedDependencies │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ OVERRIDE │
│ ├── Forces dependency version │
│ ├── Stored as version map │
│ ├── Applies to entire graph │
│ └── Registered in overrides │
└──────────────────────────────────────────────┘
A patch changes what the code does. An override changes which code is installed.
Patch Lifecycle
┌──────────────────────────────────────────────┐
│ pnpm patch <pkg>@<version> │
│ → Extract to /tmp/pnpm-patch-xyz │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Edit files in the temporary directory │
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ pnpm patch-commit /tmp/pnpm-patch-xyz │
│ → patches/<pkg>@<version>.patch │
│ → patchedDependencies in pnpm-workspace.yaml│
└──────────────────┬───────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ pnpm install │
│ → Patch applied automatically │
└──────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Patch command | pnpm patch <pkg>@<version> |
| Commit command | pnpm patch-commit <path> |
| Patch location | patches/ directory |
| Patch registration | patchedDependencies in pnpm-workspace.yaml |
| Override registration | overrides in pnpm-workspace.yaml |
| Patch priority | Exact version > range > name |
| Unused patch setting | allowUnusedPatches (default false) |
| Patch removal | pnpm patch-remove <pkg>@<version> |
Key takeaways:
- Patches modify source code; overrides change versions. A patch is for a bug fix or a code change. An override is for forcing a transitive dependency to a specific version.
- Do not use patches to change dependencies. The pnpm documentation explicitly warns against modifying a package’s
package.jsonto add dependencies. UseoverridesorpackageExtensionsinstead . - Patches are version-specific. A patch for
lodash@4.17.21does not apply tolodash@4.17.22. When the dependency is upgraded, the patch must be updated or regenerated. - The
patches/directory must be committed. The patch file is part of the project’s configuration. Without it,pnpm installon a fresh clone will fail or skip the patch. - Overrides affect the entire dependency graph. A single
overridesentry can force a version across every package that depends on the overridden package.
Remember: A patch is a temporary divergence. It should be minimal, documented, and removed when the upstream fix lands. An override is a version constraint that resolves a conflict or a vulnerability. Both are registered in pnpm-workspace.yaml, both are applied on pnpm install, and both are committed to Git. The exam tests the workflow: extract, edit, commit for patches; declare, verify for overrides.
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!