Upgrading React Native can look straightforward when the JavaScript code continues to compile. The difficult part usually appears later, when the application is tested on Android and iOS devices.
React Native applications depend on more than JavaScript. A typical project includes native Android and iOS code, platform-specific libraries, build tools, permissions, navigation, push notifications, storage, and native modules. A React Native upgrade can therefore introduce a regression on one platform while the other continues to work normally.
The safest approach is to treat a React Native upgrade as a cross-platform compatibility project. Test the JavaScript layer, native build, device behavior, third-party modules, and release builds before allowing the new version into production.
Why React Native Upgrades Need Platform-Specific Testing
A React Native application has several layers:
React Native
↓
JavaScript / TypeScript
↓
Native Modules
↓
Android / iOS
↓
Device APIs
A change at one layer can affect another.
For example, a dependency may work correctly in JavaScript but fail during an Android build because its native module is incompatible with the updated React Native version.
Similarly, an iOS application may build successfully but fail at runtime because a native library requires a different configuration.
This is why:
npm test
or:
npx react-native start
is not enough to validate a React Native upgrade.
You need platform-specific testing.
Start With a Baseline
Before upgrading React Native, record the versions currently used by the project.
Check React Native:
npx react-native --version
Check the project's dependencies:
npm ls react react-native
For Yarn projects:
yarn why react-native
Also record the native toolchain versions.
For Android, check:
cd android
./gradlew --version
For iOS, inspect the Xcode version:
xcodebuild -version
Keep these details in the migration notes.
A useful baseline includes:
Component | Current version |
|---|
React Native | Existing project version |
React | Existing project version |
Node.js | Existing version |
npm/Yarn | Existing version |
Android Gradle Plugin | Existing version |
Gradle | Existing version |
Kotlin | Existing version |
Xcode | Existing version |
CocoaPods | Existing version |
The purpose is to understand exactly what changed during the upgrade.
Create a Dedicated Upgrade Branch
Create a separate branch before changing dependencies:
git checkout -b react-native-upgrade
Then install and commit the existing dependency state if it has not already been committed.
Do not combine the React Native upgrade with unrelated application changes.
For example, avoid doing this in the same change:
React Native upgrade
+
Navigation upgrade
+
New UI redesign
+
State-management migration
+
New native module
If the application breaks, identifying the cause becomes difficult.
A controlled upgrade changes as few variables as possible.
Upgrade React Native in a Controlled Environment
Follow the upgrade path supported by the React Native version you are targeting.
After changing the version, inspect the resulting dependency tree:
npm install
npm ls react-native
Then check for dependency conflicts:
npm ls
Pay particular attention to packages that contain native code.
Common examples include:
camera libraries
maps
push notifications
biometrics
secure storage
file systems
Bluetooth
background tasks
analytics SDKs
authentication SDKs
A package that has no JavaScript API changes may still require native changes after a React Native upgrade.
Build Android Separately
Do not assume that a successful JavaScript build means Android is ready.
Run the Android build:
cd android
./gradlew assembleDebug
On Windows:
cd android
gradlew.bat assembleDebug
Watch for errors involving:
Gradle
Kotlin
Android SDK
Manifest
Native modules
CMake
NDK
Java
If the build fails, identify whether the failure originates from React Native itself or a third-party native dependency.
For example, a failure in:
node_modules/some-native-library/android/
is different from a failure inside the React Native Android project.
This distinction makes troubleshooting much faster.
Test the Android Application on a Real Device
An emulator is useful, but it should not be the only Android test environment.
Test at least one physical Android device before release.
Verify:
application startup
navigation
keyboard behavior
permissions
camera
notifications
background behavior
deep links
file access
network requests
screen rotation
app resume behavior
Also test a device configuration representative of your users.
A high-end emulator can hide performance problems that appear on lower-end devices.
Build iOS Separately
iOS has its own native dependency and build pipeline.
After updating React Native, reinstall CocoaPods dependencies:
cd ios
pod install
Then build using Xcode or the project's normal command.
For example:
npx react-native run-ios
For production-oriented testing, open the workspace in Xcode and perform a release build.
Watch for:
CocoaPods dependency errors
Swift compilation failures
Objective-C compilation failures
Signing problems
Linker errors
Native module errors
Deployment target conflicts
A successful development build does not guarantee that the release archive will succeed.
Test iOS on a Real Device
Test the application on physical iOS hardware before release.
Pay particular attention to:
camera permissions
microphone permissions
location permissions
notification permissions
biometric authentication
background behavior
deep links
keyboard handling
safe areas
status bar behavior
push notifications
Platform-specific permission behavior is a common source of regressions because Android and iOS do not expose identical APIs or lifecycle behavior.
Test Navigation
Navigation is one of the most important application workflows.
Test:
Login
↓
Home
↓
Detail
↓
Nested screen
↓
Back navigation
↓
Deep link
Verify that navigation works after:
cold start
warm start
background resume
authentication
deep linking
notification launch
Also test hardware back behavior on Android.
For example, a screen that works correctly on iOS may have incorrect back-stack behavior on Android.
Test Native Modules Independently
Create a checklist of every native module used by the application.
For example:
Native capability | Android | iOS | Test |
|---|
Camera | Yes | Yes | Capture image |
Notifications | Yes | Yes | Receive notification |
Biometrics | Yes | Yes | Authenticate |
Secure storage | Yes | Yes | Save/read credential |
Location | Yes | Yes | Request and retrieve location |
File system | Yes | Yes | Read/write file |
Deep links | Yes | Yes | Open target screen |
Do not mark a module as compatible simply because the application compiles.
Execute the actual feature.
For example, if the application uses biometric authentication, test:
No biometric configured
↓
Biometric available
↓
Successful authentication
↓
Failed authentication
↓
User cancels
↓
Fallback authentication
This is much more useful than testing only the happy path.
Test Permissions
React Native applications frequently depend on native permissions.
Test both the first-run and subsequent-run behavior.
For example:
Fresh installation
↓
Permission requested
↓
Allow
↓
Feature works
Then:
Fresh installation
↓
Permission requested
↓
Deny
↓
Feature shows correct fallback
Also test when the user changes the permission later from the device settings.
Permission handling can differ between Android and iOS, so do not assume that identical JavaScript code produces identical behavior.
Test Push Notifications
Push notifications deserve dedicated testing because they involve application state and native services.
Test:
App open
App in background
App terminated
Notification tapped
Notification dismissed
Deep link from notification
Verify that the correct screen opens after the notification is tapped.
Also test notification permissions after a fresh installation.
If notification behavior changed after the upgrade, inspect both the React Native notification library and the underlying platform configuration.
Test Background and App Lifecycle Behavior
React Native applications can behave differently depending on whether they are:
active
backgrounded
suspended
terminated
relaunched
Test:
Open application
↓
Start long-running workflow
↓
Send app to background
↓
Return to application
↓
Verify state
Check whether:
network requests are duplicated
timers behave incorrectly
authentication state is lost
subscriptions remain active
UI state resets unexpectedly
These issues may not appear during a simple development session.
Test Keyboard and Input Behavior
Keyboard behavior is another area where platform differences can become visible.
Test:
For forms near the bottom of the screen, verify that the keyboard does not cover the active input.
Test the same screen on Android and iOS because keyboard behavior can differ significantly between platforms.
Check Performance After the Upgrade
A React Native upgrade can change application performance even when functionality remains correct.
Measure:
Area | What to check |
|---|
Startup | Time from launch to usable screen |
Navigation | Screen transition responsiveness |
Lists | Scrolling and rendering |
Memory | Long-running application usage |
Animations | Dropped frames or stuttering |
Network | Request and rendering timing |
Battery | Background activity |
Large lists are particularly useful for regression testing.
For example, test a screen containing hundreds or thousands of records rather than only a five-item development dataset.
A screen that appears fast during development can become noticeably slower with production-sized data.
Test Release Builds
Do not stop at debug builds.
Create release builds for both platforms.
Android:
cd android
./gradlew assembleRelease
For a production distribution workflow, use the project's normal signing and bundle process.
iOS should be tested using an appropriate Release configuration in Xcode.
Release testing can expose problems related to:
A feature that works in Debug but fails in Release is still a release blocker.
Check CI/CD
The upgrade should also work in the automated build environment.
Verify:
Node.js version
Java version
Android SDK
Gradle
CocoaPods
Xcode
Ruby dependencies
Package manager
React Native version
A useful CI pipeline should build both platforms where appropriate.
For example:
steps:
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build Android
run: cd android && ./gradlew assembleRelease
The iOS pipeline should perform the equivalent dependency installation, testing, and build process on macOS infrastructure.
Common Platform Regression Patterns
Android build succeeds but runtime crashes
Usually investigate native module compatibility, ProGuard/R8 configuration, permissions, or release-only behavior.
iOS build fails after CocoaPods installation
Check pod versions, deployment targets, native module compatibility, and Xcode requirements.
Only one platform fails
Do not immediately change shared JavaScript code.
First determine whether the failing feature uses platform-specific native code.
Debug works but Release fails
Compare environment variables, native configuration, signing, and optimization settings.
Notifications stop working
Check notification permissions, native configuration, token registration, and the notification library's compatibility with the new React Native version.
Best Practices for React Native Upgrades
Practice | Why it helps |
|---|
Upgrade on a dedicated branch | Simplifies rollback |
Record baseline versions | Makes regressions easier to identify |
Update one major dependency at a time | Reduces debugging scope |
Test Android and iOS separately | Finds platform-specific failures |
Use real devices | Exposes hardware-specific problems |
Test native modules | Validates actual device functionality |
Build Release versions | Finds production-only issues |
Test CI/CD | Prevents deployment surprises |
Keep rollback artifacts ready | Reduces release risk |
Advantages and Disadvantages
Advantages
Identifies platform regressions before production.
Makes native dependency problems easier to isolate.
Improves confidence in Android and iOS release builds.
Encourages better testing of device-specific behavior.
Creates a repeatable upgrade process for future React Native versions.
Disadvantages
Requires testing on multiple platforms.
Native build failures can take longer to diagnose.
Third-party native modules may require additional upgrades.
Real-device testing increases the time required for validation.
CI/CD environments may need toolchain updates.
React Native Upgrade Testing Checklist
[ ] Current React Native version recorded
[ ] React and React Native versions verified
[ ] Node.js and package manager versions recorded
[ ] Android toolchain checked
[ ] iOS toolchain checked
[ ] Upgrade tested on a separate branch
[ ] JavaScript tests pass
[ ] Android Debug build passes
[ ] Android Release build passes
[ ] iOS Debug build passes
[ ] iOS Release build passes
[ ] Real Android device tested
[ ] Real iOS device tested
[ ] Navigation tested
[ ] Permissions tested
[ ] Notifications tested
[ ] Native modules tested
[ ] Deep links tested
[ ] Keyboard and forms tested
[ ] Background behavior tested
[ ] Performance compared
[ ] CI/CD builds successfully
[ ] Rollback plan prepared
Summary
A React Native upgrade should be tested as a platform migration, not simply as a JavaScript dependency update.
The most important areas are native modules, Android and iOS builds, permissions, notifications, navigation, lifecycle behavior, release builds, and real-device testing. These are the areas where an application can appear healthy during development while still failing in production.
The safest process is to establish a baseline, upgrade in isolation, test Android and iOS independently, verify native functionality, compare performance, and run the complete release pipeline before deployment.
A React Native upgrade is successful when the application continues to behave correctly across the platforms and device scenarios that matter to its users—not merely when the new version installs without errors.
Join the conversation! Your thoughts help the community grow.