The React Native upgrade timeline: what 6 versions behind actually means
One version behind costs roughly a week of focused engineering. Three versions behind costs three weeks if you're lucky. Six versions behind costs 4 to 12 weeks of elapsed work and is the threshold where in-house attempts most often stall.
The cost curve isn't linear. It's flat-then-jagged: each version step crosses a different set of breaking changes, build-tool jumps, and dependency cliffs.
The most predictive signal of how long your upgrade will take isn't the React Native version distance. It's the maintenance state of your dependency graph and the number of custom native modules.
"We're four versions behind on React Native." That sentence does almost nothing to predict how long the upgrade will take. Four versions could be a one-week project or a three-month one, depending on what else is in the codebase.
This post is the patterns we've seen across rescue engagements. The work is concentrated, the stall points are predictable, and the cost curve is jagged in places worth knowing about in advance.
The curve isn't linear
It's tempting to think "if one version takes a week, six should take six weeks." That's not the shape.
The first version step is mostly straightforward: the upgrade helper diff is small, dependencies usually slot in, the New Architecture flag stays put. Each additional version step adds work that scales worse than linearly because:
- The cumulative breaking-change list compounds
- You cross different build-tool requirement floors (Gradle versions, JDK versions, Xcode requirements)
- You cross into different default architectures (New Architecture flipped on at 0.76)
- Dependency peer ranges that worked for a small jump fail for a large one
- Patch-package patches that survived one version step often don't survive several
The result: the cost curve is flat for the first hop, jumps at certain version boundaries, then flattens again.
Cost by version distance
Rough engineering-week estimates from actual engagements. Variance is large; treat these as central tendencies, not promises.
| Versions behind | Typical engineering weeks | Notes |
|---|---|---|
| 1 (e.g. 0.75 → 0.76) | 0.5–2 | Single-version. Upgrade helper is well-behaved. |
| 2 (e.g. 0.74 → 0.76) | 1–3 | Often crosses the New Architecture default-on threshold. |
| 3 (e.g. 0.73 → 0.76) | 2–5 | Dependencies start needing genuine work. |
| 4 (e.g. 0.72 → 0.76) | 3–7 | Cumulative breaking changes start to compound. |
| 5 (e.g. 0.71 → 0.76) | 4–9 | Multiple library replacements likely. Build tool work required. |
| 6+ (e.g. 0.70 → 0.76) | 4–12 | The in-house stall zone. Productized engagement compresses time. |
| 10+ (e.g. 0.66 → 0.76) | 8–16 | Effectively a partial rewrite of dependencies. Plan accordingly. |
The "engineering weeks" is focused engineering — one or two engineers working on the upgrade as their primary task. In-house teams trying to do the upgrade in spare time alongside features typically multiply elapsed time by 2 to 4.
The real drivers of cost
Three things predict the timeline far better than version distance alone.
Dependency graph health
How many libraries in your package.json are still maintained? An app with 25 dependencies and 24 of them current upgrades fast. An app with 25 dependencies and 8 of them abandoned takes much longer — each abandoned library is a small replacement project.
The free scanner against your lockfile will tell you which way you fall before you commit to an upgrade.
Custom native modules
Each custom native module written against the old NativeModule API needs migration to TurboModules for the New Architecture. A module that was 50 lines is a few hours. A module that's 2,000 lines with custom threading or event dispatch is days to weeks.
The team count matters too — most teams have 2–4 custom native modules they barely remember writing.
Patch-package depth
Patches are diffs against specific versions of files. The further you jump versions, the more patches stop applying. Each broken patch becomes a re-author, a delete, or a hunt for the upstream fix that may or may not exist.
An app with 3 patches is a quick triage. An app with 20 patches is a week of investigation work before the actual framework upgrade even starts.
Common stall points
Observed patterns where in-house upgrade attempts get stuck for weeks or months.
- The Reanimated 2-to-3 migration. Required at New Architecture, but the migration touches many components and feels open-ended. Teams hit it, realize they have to refactor every animated screen, and pause.
- One critical abandoned library. Often a payment SDK wrapper, an enterprise auth library, or a custom UI library. No drop-in replacement. Team spends weeks trying to upgrade, fork, or rewrite around it.
- Custom native module rewriting. The team knew there was a custom native module. They didn't know what was in it. Migration to TurboModules requires reverse-engineering the old code's intent.
- Gradle and JDK jumps. Old React Native versions used Gradle 6 or 7. Current uses Gradle 8. JDK 11 → 17. Each jump fails Android builds in unrelated ways.
- Xcode and Cocoapods drift. Old projects assume Xcode 14, current needs 15 or 16. iOS pods need updates that cascade.
- Privacy manifest backfill. If you're upgrading past 0.73, you also need privacy manifests on all third-party SDKs. Half of them ship one; half don't.
The half-life of an in-house upgrade attempt
An in-house React Native upgrade that doesn't ship within its initial estimate has a high abandonment rate. The shape we've seen:
- Initial estimate: "two sprints"
- Six weeks in: a critical library doesn't have a compatible version. Discussion pauses.
- Three months in: feature work has resumed. The upgrade branch is days behind
mainand stale. - Six months in: the upgrade branch is unmergeable. Quietly abandoned.
- Twelve months later: the upgrade is attempted again, from a starting point that's drifted further.
Anecdotally, roughly half of stalled in-house upgrades get abandoned and restarted later, rather than finished. The restart pays the cost of the first attempt plus the cost of additional drift.
Successful in-house upgrades have one thing in common: someone is given the upgrade as their only task for the duration. Upgrades attempted as a side-project alongside feature work stall at a rate so consistent it's essentially predictive.
Predicting your timeline
A rough formula based on the drivers above:
- Start with the version-distance baseline from the table above.
- Add 1 week per abandoned dependency that needs replacement.
- Add 1–4 weeks per custom native module needing TurboModule migration (depending on size).
- Add 1 week per 10 patch-package patches you're carrying.
- Multiply by 1 for focused engineering, 2 for split attention, 3+ for interleaved feature work.
A typical example: a team 5 versions behind, with 6 abandoned dependencies, 3 custom native modules (one large), and 12 patches, working with split attention.
- Baseline: 5 weeks
- Dependencies: +6 weeks
- Native modules: +1 + 1 + 4 = 6 weeks
- Patches: +1 week
- Subtotal: 18 weeks of focused work
- Split attention multiplier: ~36 weeks elapsed
That's 8–9 months for an upgrade the team probably estimated at "a few sprints." The math is uncomfortable but it's better to know it upfront.
One big upgrade vs many small ones
For teams already several versions behind, the choice is whether to step through versions one at a time or jump in one go.
Stepping (one version at a time):
- Each step is small enough to reason about
- Easier to debug — the breakage is bounded to one version's changes
- More total elapsed time — you're doing the install, build, test cycle N times
- Higher risk of stalling between steps as the team gets pulled to other work
Jumping (multi-version at once):
- Single concentrated effort, less calendar overhead
- Harder to debug — when something breaks, was it the 0.71 change or the 0.73 change?
- Lower total engineering time if it works smoothly
- Less risk of mid-upgrade interruption
For 1–3 versions behind, stepping is usually right. For 4+ versions, jumping is faster if you have the team focus to sustain it. For 6+ versions, productized engagements that own the work end-to-end are usually the cost-effective answer.
Want a real estimate for your specific case?
Run the free scanner against your lockfile. It'll show your dependency graph health, the libraries that have moved on, and the known CVEs in your current pins — which together are the actual drivers of the timeline, not the React Native version distance alone.
Frequently Asked Questions
How long does a React Native upgrade take?
For a single minor-version upgrade on a clean codebase, a few days. For a single minor with a moderate dependency graph, about a week. The non-linear part is when you're several minors behind — the work doesn't scale linearly with version distance, because each version step crosses into different breaking changes, build-tool requirements, and dependency boundaries. Six versions behind usually takes 4 to 12 weeks of focused work, not six times the one-version cost.
What makes a React Native upgrade take longer?
Three things, in roughly this order. First, abandoned dependencies that need replacement — every dead library is a small project. Second, custom native modules that have to migrate to TurboModules under the New Architecture. Third, accumulated patch-package patches that no longer apply cleanly and have to be either reapplied, reauthored, or removed entirely. The framework itself is usually the smallest component of the total work.
Should I upgrade one version at a time?
For staying current, yes — single-minor upgrades are dramatically less risky than multi-minor jumps. For catching up when you're already 4+ versions behind, the choice is more nuanced. Stepping by single versions means doing the upgrade work multiple times, each time on a codebase still being changed by other developers. Jumping in one big upgrade is more disruptive but takes less calendar time. The right answer depends on your team's appetite for sustained focus vs incremental commits.
What are the common stall points in a React Native upgrade?
By far the most common is a dependency that doesn't have a version supporting the target React Native — either it's abandoned or the maintainer is behind. Other recurring stalls: native build failures from Gradle or Cocoapods version mismatches, Reanimated 2 to 3 migration, patches against React Native internals that no longer apply, custom native modules that need TurboModule rewrites, and privacy-manifest gaps on third-party SDKs.
Is it better to upgrade in-house or hire help?
In-house works well when you're one or two versions behind and have engineers who can carve out two weeks of focused time. It works poorly when you're four or more versions behind and the team is also expected to ship features — those upgrades tend to stall. A productized rescue service or experienced contractor compresses calendar time and de-risks the work, at the cost of the engagement fee. The break-even is roughly: if your in-house estimate is more than 6–8 weeks of elapsed time, outside help is usually cheaper end-to-end.
What does "half-life" mean in the context of a React Native upgrade?
Half-life is how long it takes for half of stalled in-house upgrades to actually ship vs get abandoned. Anecdotally, in-house React Native upgrades that don't ship within their initial estimate have a high abandonment rate — somewhere around 50% are quietly dropped within 6 months of the initial attempt, with the team going back to the old version. The work resumes a year later under more pressure, on a codebase that's drifted even further. Recognizing the half-life pattern is how you avoid being the team that learns it twice.