skip to content

How do you upgrade a bare React Native app to a new version, and what does the Upgrade Helper give you?

level: middleimportance: must knowfreq 58%

answer

  1. three projects in one repo
  2. diff of the template between versions
  3. package.json first, then native files
  4. merge by hand, never overwrite
  5. rebuild both platforms, run tests

basics

~20 s

Bump react-native and react to the versions the Upgrade Helper's package.json shows, then hand-apply its diff of the project template to the iOS, Android and JavaScript config files, update native libraries, reinstall pods and rebuild both platforms.

solid answer

~40 s

A bare React Native app is really three projects, JavaScript, iOS and Android, and the template files in all three change between releases. The **Upgrade Helper** is a web tool that shows the full diff of the template between any two versions, with comments explaining why some lines change. I start with its `package.json`: install the `react-native` and `react` versions it names and align the `@react-native/*` dev packages. Then I apply the remaining file changes by hand, because my `AppDelegate`, `MainApplication`, `Podfile` and Gradle files carry customisations the template does not. I read the release post's breaking changes, update native libraries, run `pod install`, clean-build iOS and Android and run the tests before merging.

code

bash · 5 lines
bash
npm install [email protected] [email protected]
# apply the Upgrade Helper diff by hand to ios/, android/ and config files
cd ios && bundle exec pod install && cd ..
npm run ios
npm run android

go deeper

for a junior

Recall that an upgrade touches package.json plus native files, and that the Upgrade Helper shows the template diff between two versions.

for a middle

Explain the procedure: read breaking changes, bump packages, hand-apply the diff, update libraries, reinstall pods, rebuild and test both platforms.

for a senior

Show how you merge a heavily customised AppDelegate or Gradle file, keep commits reviewable and catch runtime regressions the diff cannot reveal.

for a principal

Frame upgrades as routine maintenance with an owner and a budget rather than a rescue project every few years.

## Why upgrading is more than one npm install A **bare** React Native app is one where the team owns the `ios/` and `android/` folders. It combines three projects: - a **JavaScript** project (`package.json`, Babel, Metro and Jest config, TypeScript config); - an **iOS** project (`Podfile`, Xcode project, `AppDelegate`); - an **Android** project (Gradle files, `gradle.properties`, `MainApplication`, the Gradle wrapper). Each release of React Native can change the **template** those files were generated from: a new Gradle or Kotlin version, a new `Podfile` helper, a changed app delegate, a new Babel preset. Bumping the npm package alone leaves the native projects on the old template, and the build fails or misbehaves. ## What the Upgrade Helper is The **Upgrade Helper** is a community web tool, linked from the React Native docs and from every release post. You choose the version you are on and the version you want, and it shows the **complete diff of the template** between them, file by file, with inline comments where a change needs explaining. It works for any two versions, but it only shows what changed in the **template**, not what changed in your app's own code or in the libraries you use. ## The procedure 1. **Read the release posts** for every version you cross, especially the **Breaking Changes** and **Deprecations** sections, and the changelog's Breaking and Removed entries. 2. **Update `package.json`** first, as the Helper lists it: `react-native`, the matching `react`, and the `@react-native/*` packages such as the Babel preset, Metro config and TypeScript config. 3. **Apply the other file changes by hand.** For each file the Helper shows, apply the same edit to your version of it. Do not paste the template's file over yours: your `AppDelegate`, `MainApplication`, `Podfile` and `build.gradle` usually carry analytics setup, deep-link handling, flavors or signing that the template does not know about. 4. **Update native libraries** to versions that support the target React Native version. 5. **Reinstall and rebuild**: install JS packages, run `pod install` in `ios/`, clean both native builds, and build iOS and Android. 6. **Test**: unit tests, a smoke pass on both platforms, and the flows most exposed to the release's breaking changes. ## Examples of what the diff contains | Release | Template or config change you would see | |---|---| | 0.85 | Jest config switches to `preset: '@react-native/jest-preset'` | | 0.87 | `compileSdk` and build tools raised to 37 | | 0.87 | recommended AGP 9 opt-outs added to `android/gradle.properties` | The release posts explain the reason for each; the Helper shows the exact lines. ## Hand-merging well - Apply changes in **small commits per file group**, so a broken build points at one area. - When a changed file is heavily customised, re-derive the edit from the comment and the release notes, not from line positions. - Keep a **record of your deliberate deviations** from the template; the next upgrade needs it. - If the build still uses old code after all changes, the docs point first at **caches**: clean the native build folders and restart Metro with its cache cleared before debugging further. ## After the upgrade Commit the lock files (`package-lock.json` or equivalent, `Podfile.lock`) so CI builds the same graph. Watch the first release on the new version closely: runtime behaviour changes, not compile errors, are what escape testing. ## Where upgrades usually go wrong - **Only `react-native` is bumped.** `react` must match the version the release pins (0.87.1 peer-depends on `react` `^19.2.3`), and the `@react-native/*` packages such as `@react-native/babel-preset`, `@react-native/metro-config` and `@react-native/typescript-config` should move with it. - **The diff is pasted, not merged.** A replaced `MainApplication` or `AppDelegate` loses app code and fails in ways that look unrelated to the upgrade. - **Release notes are skipped.** Removals such as `InteractionManager` in 0.87 show up as runtime or type errors in your own code, which no template diff mentions. - **Pods and Gradle caches are stale.** Old build outputs survive the change and make the app appear not to have upgraded at all. - **Only one platform is tested.** A change can compile on iOS and fail on Android, or the reverse, because each platform's template changes independently.

  • Why not just copy the new React Native template's native files over yours?
    Your `AppDelegate`, `MainApplication`, `Podfile` and Gradle files contain app-specific code, such as deep-link handling, SDK initialisation, flavors and signing, that the template lacks. Overwriting them silently drops that code. Applying the Upgrade Helper's diff line by line keeps your customisations and adds only what the release changed.
  • What does the Upgrade Helper not tell you during a React Native upgrade?
    It diffs the template only. It says nothing about your own code that uses removed APIs, about native libraries that need new versions, or about runtime behaviour changes. For those you read the release posts' Breaking Changes and Deprecations, the changelog and each library's release notes.

saying these in an interview costs you the question

  • Upgrading is just npm install react-native@latest
  • Paste the new template's AppDelegate and MainApplication over yours
  • The Upgrade Helper also lists your libraries' breaking changes
  • Native folders never change between React Native minors
  • If the Helper shows no JS changes, the app code needs nothing