← Back to list

React Native 0.72 → 0.76: Lessons From the Trenches

This post documents my journey of upgrading React Native from 0.72 to 0.76. Rather than a step-by-step guide, think of this as a collection…

Abijith B · 2025-09-23 17:21 · 261 claps · 8.3 min read
#react #react-native #android #ios #mobile-app-development
Open on Medium ↗
Wiki topics: 🌐 · Web Development 📱 · Mobile Development

React Native 0.72 → 0.76: Lessons From the Trenches

Photo by National Library of Scotland on Unsplash

Photo by National Library of Scotland on Unsplash

This post documents my journey of upgrading React Native from 0.72 to 0.76. Rather than a step-by-step guide, think of this as a collection of insights gained while tackling various errors. The solutions I found should be helpful for your future workflows and can act as a common triage guide.

Why React Native Upgrade?

Upgrading is not optional, it is often mandatory. The Google Play Store enforces policies that require apps to support the latest Android API levels.

In the past, I could simply update the version in build.gradle to meet the requirement. This time it was different. To support Android API 35, the project needed to be on React Native 0.76 or above. So the upgrade was not just about staying current, it was necessary to comply with Play Store requirements.

Gearing Up

My seniors always said upgrading React Native was painful. Noob me did not really get that, I thought it was just a skill issue. I had done regular React updates before and it was not a big deal.

They told me their old process: create a brand new React Native project, then copy files over into it. Oh man, what an effort. Later I saw multiple discussions on Reddit where people were doing the same thing.

Some folks pointed out that from React Native 0.7x onward, things are a bit easier. You do not always need to create a new project just to upgrade. That gave me some relief. Let’s see, how will this go!

First Step

The React Native team provides a tool called **React Native Upgrade Helper**. It basically shows you all the files that need to be changed. By going through those files, you quickly realize why a React Native upgrade feels so different from a plain React upgrade. In a React upgrade, it is mostly about updating React itself, related packages, and maybe making a few code changes if there are breaking changes. So your package.json changes a bit, maybe some of your code too, and that is it.

With React Native, it is a whole different story. Yes, you upgrade packages, but you also have to touch native code, update build files, and handle new requirements (like the iOS privacy file). If you have made any extra configurations or custom native code, those need upgrading as well. I will break this down in the coming sections. For now, I am skipping project-specific changes and only sharing the parts that are useful for everyone. I also used Claude Code during this upgrade, and it was super helpful. It made understanding error messages easier, reading files clearer, and gave me a smooth loop: read the error, edit the file, test, repeat until fixed.

Decision

I decided to go with 0.76 because it immediately solves our problem and many people suggested doing smaller upgrades. The plan was to move to 0.76 first, then later step up to 0.78 and eventually to 0.81. Right now 0.81 is the latest version, and we are starting from 0.72. Jumping straight from 0.72 to 0.81 would mean a huge set of changes and some very heavy lifting.

I also chose to stick with the old architecture. The reason is simple: all our packages currently support it. I am not sure which ones fully support the new architecture yet, and if something does not, I would be stuck finding or replacing packages. For our use case, performance has not been an issue so far. Of course, depending on your app, you might have a different opinion here.

Since we are an early stage startup, we cannot afford to get stuck for days just upgrading packages. So my plan going forward is to treat upgrades like a regular exercise, maybe every quarter or at least once in six months. That way, the jump will never be too big and it should feel more manageable.

Starting

The React Native Upgrade Helper was my starting point. It shows exactly what files need to be changed, and that gave me a clear path. They also provide a handy command:

npx @rnx-kit/align-deps --requirements react-native@0.76

This automatically aligns your dependencies with React Native 0.76. That was really helpful, because it meant one less thing to worry about. Of course, you still need to review the updated package versions yourself and check if any of them introduce breaking changes that affect your code.

After that, a quick yarn install went through fine.

One change I had to make was with Java. I was using Java 11 earlier, but React Native 0.76 requires Java 17. I manage my Java versions using **SDKMAN**, so installing and switching to Java 17 was straightforward.

There were a few issues along the way:

  • Flipper issue: Flipper is no longer needed, so this was removed.
A problem occurred evaluating project ':app'.
> Could not get unknown property 'FLIPPER_VERSION' for object of type 
  org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.
  • Missing native_modules.gradle: This script was also no longer required and was removed.
A problem occurred evaluating project ':app'.
> Could not read script '/Users/abijith.b/code/capital-fe/packages/uc-app/node_modules/@react-native-community/cli-platform-android/native_modules.gradle' as it does not exist.
  • Reanimated version compatibility:

The version I had was requiring React Native 0.78, so I had to downgrade the Reanimated library. Here is the Reanimated compatibility table.

After all this, the app is working fine on Android 14 and 15. Essential features like Camera, GPS, and File Upload are all functioning as expected.

iOS Build Issues

While upgrading React Native, I faced multiple issues with the iOS build:

  • By default, new architecture was enabled for iOS and Android in the React Native Upgrade Helper. I had to manually turn it off.
  • Updated Podfile dependencies.
  • Added the new required file PrivacyInfo.xcprivacy.
  • The build was stuck with multiple errors like:
Command SwiftGeneratePch emitted errors but did not return a nonzero exit code to indicate failure module map file '/Users/abijith.b/Library/Developer/Xcode/DerivedData/Pratishtha-aqflutwkdvtfiyeyqqtmhmsgdgbf/Build/Products/Debug-iphoneos/VisionCamera/VisionCamera.modulemap' not found Module map file '/Users/abijith.b/Library/Developer/Xcode/DerivedData/Pratishtha-aqflutwkdvtfiyeyqqtmhmsgdgbf/Build/Products/Debug-iphoneos/VisionCamera/VisionCamera.modulemap' not found Command PrecompileSwiftBridgingHeader emitted errors but did not return a nonzero exit code to indicate failure
  • There were around 10 such errors, mostly modulemap not found for different packages.

I tried the usual cleanup steps:

# Xcode
# Product -> Clean Build Folder

# Clean iOS build artifacts
cd ios
rm -rf build/
rm -rf Pods/
rm Podfile.lock

# Reinstall dependencies
pod install --repo-update

# If needed:
pod deintegrate
pod setup
pod install

# Clean Xcode's derived data
rm -rf ~/Library/Developer/Xcode/DerivedData/
  • Updated Xcode from 16.3 → 16.4.
  • Despite all this, the same errors persisted that day.
  • The next morning, strangely, the errors disappeared, and the build progressed. I didn’t restart though!
  • A new issue appeared, this time related to Lottie. After updating the Lottie package, the build worked fine.
  • The app was then uploaded to the App Store and tested successfully on iOS.

Polishing

The main functionality is working fine, now it’s down to finishing touches. On Android 15, edge-to-edge display means app content can overlap with the status bar. To handle this, you can use the package **react-native-context-area. In my case, I didn’t want content overlap, so I applied our app’s primary color** to the status bar and the bottom navigation area.

Through testing

On iOS, CodePush was giving a bundle size of 6MB compared to the earlier 3MB, while Android was still around 3.20MB. As per Claude’s suggestion, I downgraded Lottie, but that did not help. I also explicitly set the CodePush release script to run in release mode with development set to false using CLI flags, but that did not work either. The app was still functioning fine though. It turned out Claude had made the Podfile unnecessarily complicated in an earlier build issue. All we really needed was to disable the new architecture by adding:

ENV[‘RCT_NEW_ARCH_ENABLED’] = ‘0’

No other changes were required. I even removed an extra fix we used in 0.72 related to stripping bitcode. So yes, Claude can help you sometimes, but it can also waste a lot of your time.

On Android, CodePush was not working at all. The app was crashing on update, so I started debugging with adb logcat. It turned out the system was looking for a JS bundle named null. The fix was to properly configure CodePush in MainApplication.kt, which Claude had missed during migration. Even after doing that, CodePush still was not working. Eventually, I found another issue with react-native-vision-camera. It was trying to access the camera while the app was switching to the new JS bundle. I tracked down the code and created a patch using patch-package.

Failing Packages & Identical Error Pattern

Three packages were failing with identical C++ standard library linking errors: react-native-vision-camera, react-native-reanimated, and react-native-screens. The core error symptoms were the same across all of them. The build logs showed undefined symbols for fundamental C++ operations like:

- `operator new(unsigned long)` / `operator delete(void*)`
- `__cxa_allocate_exception` / `__cxa_throw` / `__cxa_begin_catch`
- `std::mutex::lock()` / `std::mutex::unlock()`
- `std::__ndk1::__shared_weak_count::__release_weak()`
- `std::basic_string` operations
- `std::runtime_error` / `std::length_error`
- RTTI symbols (`vtable for __cxxabiv1::*`)

The error pattern analysis made it clear that the root cause was the C++ standard library not being properly linked during native module compilation. This was not package-specific but a system-wide build failure. The linker, ld.lld, was consistently failing with “undefined symbol” errors.

In desperation, I tried multiple things: downgrading the NDK from 26 to 25, downgrading the packages, and even checking out older commits. Nothing worked. At one point I was seriously asking, why god, why me? Then I came across a comment in the React Native Reanimated repo where someone suggested reinstalling the NDK. Just to try, I upgraded from 26 to 27, and it actually worked.

That is when I understood what had happened. While debugging a patch package for React Native Vision Camera, Claude had created a patch. After trying to install it, the NDK somehow got corrupted. Deleting the NDK from ~/Library/Android/sdk/ndk/... and rebuilding the APK will download the NDK again. Unfortunately, I had already wasted one and a half days figuring this out.

Once I confirmed the fix, I also cleaned things up. I removed the large patch file. deleted build artifacts from node_modules inside the Vision Camera package, and then reinstalled everything clean with yarn install --force. After that, I reapplied my code changes in react-native-vision-camera and created a fresh patch using yarn patch-package react-native-vision-camera.

# Clean build artifacts from node_modules
rm -rf node_modules/react-native-vision-camera/android/.cxx
rm -rf node_modules/react-native-vision-camera/android/build

Publishing

iOS has not yet accepted the build, but Android gave us a hard time. First, it wanted us to remove the READ_IMAGE permission. Fixed it by updating the library, which dropped the permission automatically. I also made sure to remove it from the Manifest file.

After that, I submitted a new build, but the Play Store rejected it, saying the build number was already in production. I honestly could not understand what exactly they meant by that, so I went ahead and released the update directly to production instead of going through open testing. Other people also had this issue. Better don’t do this, I couldn’t control the rollout percentage here. Yay! It’s a wrap.

My takeways

You are definitely going to get build errors, plenty of them, both on Android and iOS. Sometimes you will not find those errors documented anywhere. No help from AI, no question on StackOverflow, nothing on Reddit. And it is not just you. Many other developers have gone through the same pain. The key is to stay calm and keep going, you will eventually find a way.

AI can help, but it can also create issues. Every change it suggests needs to be verified carefully, because at the end of the day you are the one responsible for the code that gets merged into the repo.

Testing should cover all possible OS versions. After release, keep a close eye on crashes using Crashlytics. With big changes, there can be crashes on certain devices, so you need to monitor and respond quickly.

Finally, do not expect publishing on the Play Store or App Store to get approved immediately. Policies change and reviews can get stricter, so rejection is always a possibility. You cannot fast-forward the review process. When giving an ETA, always include buffer time for potential rejection and resubmission. A good approach is to submit an early version while you are still testing. That way, if the Play Store or App Store rejects it due to policy, you get that feedback early instead of waiting until the very end.

That is it from my side. Good luck and success to all developers upgrading and fixing issues. Stay strong, you got this. Bye ✌️


메타데이터
post_id
0a548ba6b83f
slug
react-native-0-72-0-76-lessons-from-the-trenches-0a548ba6b83f
url
https://medium.com/@abijith.b/react-native-0-72-0-76-lessons-from-the-trenches-0a548ba6b83f
canonical_url
https://medium.com/@abijith.b/react-native-0-72-0-76-lessons-from-the-trenches-0a548ba6b83f
author_url
https://medium.com/@abijith.b
status
ok
fetched_at
2026-07-17 08:17:15