patch-package: The Lifesaver for both Bleeding Edge and Legacy Projects
patch-package lets you keep a local diff against an installed dependency and replay it on every install — useful when an upstream bug blocks you and you can't wait for a fix (or there is no maintainer left to wait for). In 2026 most package managers ship this natively; patch-package is now the cross-tool / npm fallback rather than the only answer.
Native first
| Package manager | Command |
|---|---|
| Bun | bun patch <pkg> (since 1.0) |
| pnpm | pnpm patch <pkg> |
| Yarn 3+ | yarn patch <pkg> |
| npm | no native equivalent |
Each one writes a patch file alongside package.json and replays it on install. If you're on Bun, pnpm, or Yarn, prefer the native path — fewer moving parts.
patch-package, when you're stuck on npm
1bun add -d patch-packageEdit the offending file inside node_modules/<pkg> directly. Then generate the patch:
1bunx patch-package <pkg>That writes patches/<pkg>+<version>.patch — commit it. Wire up a postinstall hook so any future install replays the patch:
1{
2 "scripts": {
3 "postinstall": "patch-package"
4 }
5}A real case: Yoga + Xcode 14.3 (2023)
A concrete example from mid-2023, kept here as illustration. Xcode 14.3's bundled Clang got stricter about implicit conversions, and older React Native versions wouldn't build because Yoga's Yoga.cpp used a bitwise & where the new compiler wanted a logical &&. Upgrading React Native on a non-Expo bare project wasn't trivial, so:
- Edit
node_modules/react-native/ReactCommon/yoga/yoga/Yoga.cpp— fix the operator. bunx patch-package react-native→ writespatches/react-native+<version>.patch.- Commit the patch file and the
postinstallhook. - Every fresh install re-applies the fix; CI builds again.
Same shape for any "stuck on an old version, blocked by one line in a dep" situation. Once upstream lands the fix and you upgrade, delete the patch.