Skip to main content

patch-package vs pnpm patch vs yarn patch: Patching node_modules (2026)

·PkgPulse Team

TL;DR

patch-package is the original tool for patching npm dependencies — edit files in node_modules/, run npx patch-package <pkg>, and it saves a .patch file that auto-applies on install. pnpm patch is pnpm's built-in patching — same workflow, integrated into the package manager, stored in patches/. yarn patch is Yarn Berry's equivalent — yarn patch <pkg> opens an editable copy, yarn patch-commit saves the diff. In 2026: use your package manager's built-in patching (pnpm patch or yarn patch) if possible, fall back to patch-package for npm.

Key Takeaways

  • patch-package: ~3M weekly downloads — works with npm, yarn classic, pnpm, package-manager-agnostic
  • pnpm patch: built into pnpm — zero extra dependencies, pnpm patch / pnpm patch-commit
  • yarn patch: built into Yarn Berry (v2+) — yarn patch / yarn patch-commit
  • Patches are git-trackable .patch files — your team gets the fix automatically
  • All three auto-apply patches on install — postinstall hook or built-in integration
  • Always file an upstream PR — patches are temporary fixes, not permanent solutions

When to Patch Dependencies

Patch when:
  ✅ Critical bug fix — upstream hasn't released yet
  ✅ TypeScript type errors — @types package has wrong types
  ✅ Security vulnerability — can't upgrade (breaking changes), patch the specific issue
  ✅ Framework compatibility — small adjustment for your specific use case

Don't patch when:
  ❌ Feature additions — fork the package instead
  ❌ Major changes — too hard to maintain across version updates
  ❌ Upstream fix is available — just upgrade

Always:
  1. File an issue/PR upstream
  2. Add a comment in the patch explaining WHY
  3. Check on every dependency upgrade if the patch is still needed

patch-package

patch-package — package-manager-agnostic:

Setup

npm install -D patch-package

# Add postinstall hook to auto-apply patches:
# package.json:
{
  "scripts": {
    "postinstall": "patch-package"
  }
}

Create a patch

# 1. Edit the file directly in node_modules:
#    Fix the bug in node_modules/some-package/dist/index.js

# 2. Create the patch:
npx patch-package some-package

# Creates: patches/some-package+1.2.3.patch
# This is a standard unified diff file

# 3. Commit the patch:
git add patches/
git commit -m "fix: patch some-package to fix XYZ bug"

Example: fixing a TypeScript type

# Problem: @types/express has wrong types for req.query
# Edit: node_modules/@types/express/index.d.ts

# Create scoped package patch:
npx patch-package @types/express

# Creates: patches/@types+express+4.17.21.patch

Example patch file

# patches/some-package+1.2.3.patch
diff --git a/node_modules/some-package/dist/index.js b/node_modules/some-package/dist/index.js
index abc1234..def5678 100644
--- a/node_modules/some-package/dist/index.js
+++ b/node_modules/some-package/dist/index.js
@@ -42,7 +42,7 @@ function processData(input) {
-  if (input.length = 0) {
+  if (input.length === 0) {
     return [];
   }

Version-specific patches

# Patch applies to exact version by default:
# patches/react-dom+18.2.0.patch  → only applies to 18.2.0

# When you upgrade react-dom to 18.3.0:
# patch-package will WARN if the patch doesn't apply cleanly
# → Review and recreate the patch if needed

pnpm patch

pnpm patch — built-in pnpm patching:

Workflow

# 1. Start patching — pnpm creates an editable copy:
pnpm patch some-package@1.2.3

# Output:
# You can now edit the following folder: /tmp/.pnpm-patch-xxx/some-package

# 2. Edit the file in the temporary folder:
# Edit /tmp/.pnpm-patch-xxx/some-package/dist/index.js

# 3. Commit the patch:
pnpm patch-commit /tmp/.pnpm-patch-xxx/some-package

# Creates: patches/some-package@1.2.3.patch
# Updates package.json:

package.json integration

{
  "pnpm": {
    "patchedDependencies": {
      "some-package@1.2.3": "patches/some-package@1.2.3.patch"
    }
  }
}

Auto-application

# Patches apply automatically on pnpm install:
pnpm install
# → Applying patches...
# → Patched some-package@1.2.3

# No postinstall script needed — pnpm handles it natively

Removing a patch

# Remove from patchedDependencies in package.json
# Delete the patch file:
rm patches/some-package@1.2.3.patch

# Reinstall:
pnpm install

yarn patch

yarn patch — Yarn Berry (v2+) built-in:

Workflow

# 1. Start patching:
yarn patch some-package

# Output:
# ➤ YN0000: Package some-package@npm:1.2.3 got extracted to /tmp/xfs-xxx

# 2. Edit files in the extracted folder:
# Edit /tmp/xfs-xxx/dist/index.js

# 3. Save the patch:
yarn patch-commit -s /tmp/xfs-xxx

# Creates: .yarn/patches/some-package-npm-1.2.3-xxxxx.patch
# Updates package.json:

package.json integration

{
  "resolutions": {
    "some-package@1.2.3": "patch:some-package@npm%3A1.2.3#~/.yarn/patches/some-package-npm-1.2.3-xxxxx.patch"
  }
}

Auto-application

# Patches apply automatically on yarn install:
yarn install
# → some-package patched

# No postinstall hook needed

Best Practices

Always document your patches

# In your codebase, create a PATCHES.md or add comments:

# patches/some-package+1.2.3.patch
# WHY: Fixes null reference when input is empty array
# UPSTREAM: https://github.com/owner/some-package/issues/123
# PR: https://github.com/owner/some-package/pull/456
# REMOVE WHEN: some-package >= 1.2.4 is released

CI verification

// package.json (patch-package):
{
  "scripts": {
    "postinstall": "patch-package",
    "verify-patches": "patch-package --error-on-fail"
  }
}
# GitHub Actions:
- name: Install dependencies
  run: npm ci
  # postinstall runs patch-package automatically

- name: Verify patches applied
  run: npx patch-package --error-on-fail
  # Fails CI if any patch doesn't apply cleanly

Monorepo patches

# patch-package in monorepo (from root):
npx patch-package some-package --patch-dir patches/

# pnpm workspace — patches defined in root package.json:
{
  "pnpm": {
    "patchedDependencies": {
      "some-package@1.2.3": "patches/some-package@1.2.3.patch"
    }
  }
}

Feature Comparison

Featurepatch-packagepnpm patchyarn patch
Package managernpm, yarn, pnpmpnpm onlyYarn Berry only
Extra dependencyYesNo (built-in)No (built-in)
WorkflowEdit node_modules → patchpnpm patch → edit copy → commityarn patch → edit copy → commit
Auto-applypostinstall hookBuilt-inBuilt-in
Config locationpatches/ dirpackage.json patchedDependenciespackage.json resolutions
Scoped packages
Monorepo
Patch on fail--error-on-failBuilt-in errorBuilt-in error
Weekly downloads~3Mbuilt-inbuilt-in

When to Use Each

Use patch-package if:

  • Using npm as your package manager (npm has no built-in patching)
  • Need a tool that works across npm, yarn classic, and pnpm
  • Team has mixed package manager usage

Use pnpm patch if:

  • Already using pnpm — zero extra dependencies, native integration
  • pnpm's patchedDependencies config is clean and declarative
  • Patches apply automatically without postinstall hooks

Use yarn patch if:

  • Using Yarn Berry (v2+) — built-in, native workflow
  • Yarn's resolution protocol handles patching transparently

General advice:

  • Always file upstream PRs — patches are temporary
  • Add comments explaining why each patch exists
  • Review patches on every dependency upgrade
  • Keep patches small — if you need major changes, fork instead

Methodology

Download data from npm registry (weekly average, February 2026). Feature comparison based on patch-package v8.x, pnpm v9.x, and yarn v4.x.

Compare package management and developer tooling on PkgPulse →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.