Your React Native patch-package directory is a graveyard. Audit it.
Every long-lived React Native app has a patches/ directory. Most of the patches in it were written years ago, by people who've left, against versions of libraries that have since shipped a real fix.
You're carrying them through every upgrade. They break. You fix the broken patch instead of asking whether the patch was ever needed in the first place.
Audit annually. Or before every React Native upgrade. Expect to delete half of them.
Open your repo. Go to patches/. Count the files.
If there are zero, this post isn't for you. If there are three, you're probably fine. If there are fifteen and one of them is from 2022, keep reading.
patch-package is a fine tool. The problem isn't the tool. It's that patches accumulate, never get reviewed, and survive upgrades they shouldn't have survived. The patches directory is where engineering debt goes to be ignored.
Why patches accumulate
The pattern goes like this. A library has a bug. The team is on a deadline. Someone forks the file in node_modules, fixes the bug, runs npx patch-package some-library, commits the patch, ships.
Two things were supposed to happen next. The author was supposed to open a PR upstream and link to it in the patch. Someone was supposed to come back when the fix shipped and remove the patch.
Neither happens. The PR gets opened, drags through review, eventually merges. By then nobody remembers to delete the local patch. The upstream library bumps versions; the patch reapplies (or fails and gets fixed); the cycle continues.
Multiply by three years of feature development. Twelve patches. None of them documented. Half of them obsolete.
Patches are written under deadline pressure. Audits don't happen under deadline pressure. The structural fix is to audit on a schedule, not when a patch breaks.
The audit, step by step
One hour, end to end, for a typical app with 10–20 patches.
Enumerate
List every file in patches/.
Each filename is in the form library-name+version.patch. Note the version. That's the version the patch was written against.
Find the why
For each patch, find the commit that introduced it:
Read the commit message. Read the linked PR if there is one. If there's no context — not in the commit, not in a code comment, not in a README — that's information itself. It means nobody documented why this exists.
Check upstream
Open the library's npm page or GitHub. Compare:
- The version the patch is against (from the filename) vs the version installed in your
package-lock.json - The library's changelog between those two versions
- Open and closed PRs that mention the same bug or area of code
If the upstream changelog mentions a fix that matches your patch's intent, the patch is probably obsolete.
Check usage
The patch fixes a specific code path. Is your app still calling that code path?
Search your codebase for the API the patch affects. If you stopped using the feature in 2024, the patch is dead weight. Delete it.
Four categories of patch
Every patch you find sorts into one of these.
| Category | Signal | What to do |
|---|---|---|
| Fixed upstream | Library has shipped a real fix in a version >= the one you're on | Delete patch. Run install. Test. |
| Still needed | Upstream hasn't fixed it. Your app still uses the affected code path | Keep patch. Document with a comment block. Open or link the upstream PR. |
| Code path dead | Patch is real, but the feature it fixes is no longer used in your app | Delete patch. |
| Library is dead | The patched library is unmaintained or has been replaced | Plan to remove the library. Delete the patch when the library leaves. |
Most teams find their split is roughly: 30% fixed upstream, 30% still needed, 25% code path dead, 15% library is dead. Your mileage will vary.
How to safely delete a patch
Resist the urge to bulk-delete. Each patch is a behavior change against the installed library. Removing one means the unpatched code runs.
The safe procedure:
- Delete the patch file from
patches/. - Run
npm install(oryarn install). The patch no longer applies. - Build the app. Run the test suite. Manually exercise the feature the patch affected.
- Open a PR with the deletion only. One patch per PR if you're being careful. Three or four per PR if you're not.
- Ship to staging or a beta channel. Let it sit a few days. If nothing surfaces, ship to production.
If the patch was a workaround for a crash, you'll find out fast. If it was a workaround for a subtle behavior bug, you might not find out for a week. That's why incremental beats bulk.
Document what survives
The patches that survive the audit need a paper trail. The next person to look at them shouldn't have to do detective work.
Create PATCHES.md at the repo root. One section per surviving patch. Each section has:
- Filename:
library-name+version.patch - What it does: one sentence
- Why it exists: the bug, the deadline, the constraint
- Upstream status: link to the PR, issue, or "no upstream tracking"
- Remove when: the condition that makes the patch unnecessary
This is the kind of document that pays for itself the first time someone says "why does this patch exist?" three years from now.
Patches you should never write
Not every workaround belongs in patches/. Three patterns to push back on:
- Patches against React Native itself. The framework's internal layout changes every release. A patch against
react-native/Libraries/...will break on the next upgrade. Use a fork or a runtime monkey-patch instead, and accept the cost of maintaining it. - Patches against generated code. Codegen output, OpenAPI clients, schema-derived types. The patch will be regenerated away the next time someone runs the generator.
- Patches that change library behavior, not fix a bug. Adding a config option, changing a default, swapping an algorithm. Either upstream it, fork the library, or wrap it. A behavior-change patch is a fork in denial.
The right use of patch-package is a small, surgical, time-bounded fix while you wait for the real fix to ship. The wrong use is everything else.
About to upgrade React Native?
Audit the patches directory first. Every patch you delete now is one less thing to debug when files move under it. The free scanner will also flag patched libraries that have known CVEs in the version you're pinned to.
Frequently Asked Questions
What is patch-package and why do React Native apps use it?
patch-package is an npm tool that lets you commit changes to a node_modules dependency as a diff. On every install, the patches are reapplied. React Native apps use it because the ecosystem is full of libraries with bugs that need fixing, peer-range issues that need overriding, or native code that needs tweaking — and waiting for the upstream maintainer isn't always realistic. It works. It's also a debt magnet.
How do I audit my React Native patches?
Open the patches/ directory and list every patch file. For each one, find out three things: why the patch exists (commit message, PR, code comment), whether the upstream library has since released a fix, and whether the patched code path is still in use. Most teams find 30–60% of their patches are obsolete on the first pass. Remove the obsolete ones. Document the rest in a single PATCHES.md file.
Why do patches break during React Native upgrades?
A patch is a literal diff against a specific version of a file. When you upgrade a library, the file probably changed — different lines, different surrounding context, sometimes the function the patch touched is gone. The patch fails to apply. patch-package warns or fails, depending on configuration. You're then debugging a fix from two years ago that nobody on the team remembers writing.
Should I commit my patches to git?
Yes. The patches/ directory must be committed. patch-package reapplies the patches on install, so without them in version control, your install isn't reproducible. The mistake isn't committing the patches — it's never auditing them after they land.
What's the alternative to patch-package?
Three real alternatives. First, npm or yarn overrides — better when you need to pin a transitive version, worse when you need to change code. Second, forking and publishing under your scope — heavier setup, but the change lives in version control instead of as an opaque diff. Third, upstreaming the fix — slowest, best long-term outcome. patch-package itself is fine for short-lived emergency fixes; the failure mode is letting them become permanent.
How often should I audit React Native patches?
Every React Native upgrade, at minimum. The upgrade is the moment when half the underlying files change anyway, so it's the natural inflection point to ask whether each patch is still needed. Some teams add it to a quarterly hygiene checklist. The wrong answer is "whenever a patch breaks" — by that point, you're auditing under pressure.