Native project upgrade helper

Edit page

View file-by-file diffs of all the changes you need to make to your native projects to upgrade them to the next Expo SDK version.


If you manage your native projects (previously known as bare workflow), to upgrade to the latest Expo SDK, you have to make changes to your native projects. It can be a complex process to find which native file changes and what to update in which file.

The following guide provides diffs to compare native project files between your project's current SDK version and the target SDK version you want to upgrade. You can use them to make changes to your project depending on the expo package version your project uses. The tools on this page are similar to React Native Upgrade Helper. However, they are oriented around projects that use Expo modules and related tooling.

Interested in avoiding upgrading native code altogether? See Continuous Native Generation (CNG) to learn how Expo Prebuild can generate your native projects before a build.

Upgrade native project files

Once you have upgraded your Expo SDK version and related dependencies, use the diff tool below to learn about changes you need to make to your native project and bring them up to date with the current Expo SDK version.

Choose your from SDK version and to SDK version to see the generated diff. Then, apply those changes to your native projects by copying and pasting or manually making changes to the project files.

From SDK version:

To SDK version:

Native code changes from SDK 54 to 55

android/app/build.gradle
MODIFIED
1111react {
1212 entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
1313 reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
14 hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
14 hermesCommand = new File(["node", "--print", "require.resolve('hermes-compiler/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/hermesc/%OS-BIN%/hermesc"
1515 codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
1616
1717 enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean()
android/app/src/main/AndroidManifest.xml
MODIFIED
55 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
66 <uses-permission android:name="android.permission.VIBRATE"/>
77 <!-- These require runtime permissions on M -->
8 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
9 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
8 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
9 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
1010 <!-- END OPTIONAL PERMISSIONS -->
1111
1212 <queries>
1919 </queries>
2020
2121 <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" android:supportsRtl="true">
22 <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true">
22 <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|smallestScreenSize" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true">
2323 <intent-filter>
2424 <action android:name="android.intent.action.MAIN"/>
2525 <category android:name="android.intent.category.LAUNCHER"/>
android/app/src/main/java/com/helloworld/MainApplication.kt
MODIFIED
66import com.facebook.react.PackageList
77import com.facebook.react.ReactApplication
88import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
9import com.facebook.react.ReactNativeHost
109import com.facebook.react.ReactPackage
1110import com.facebook.react.ReactHost
1211import com.facebook.react.common.ReleaseLevel
1312import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
14import com.facebook.react.defaults.DefaultReactNativeHost
1513
1614import expo.modules.ApplicationLifecycleDispatcher
17import expo.modules.ReactNativeHostWrapper
15import expo.modules.ExpoReactHostFactory
1816
1917class MainApplication : Application(), ReactApplication {
2018
21 override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
22 this,
23 object : DefaultReactNativeHost(this) {
24 override fun getPackages(): List<ReactPackage> =
25 PackageList(this).packages.apply {
26 // Packages that cannot be autolinked yet can be added manually here, for example:
27 // add(MyReactNativePackage())
28 }
29
30 override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
31
32 override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
33
34 override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
35 }
36 )
37
38 override val reactHost: ReactHost
39 get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost)
19 override val reactHost: ReactHost by lazy {
20 ExpoReactHostFactory.getDefaultReactHost(
21 context = applicationContext,
22 packageList =
23 PackageList(this).packages.apply {
24 // Packages that cannot be autolinked yet can be added manually here, for example:
25 // add(MyReactNativePackage())
26 }
27 )
28 }
4029
4130 override fun onCreate() {
4231 super.onCreate()
android/gradle/wrapper/gradle-wrapper.properties
MODIFIED
11distributionBase=GRADLE_USER_HOME
22distributionPath=wrapper/dists
3distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
3distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
44networkTimeout=10000
55validateDistributionUrl=true
66zipStoreBase=GRADLE_USER_HOME
android/gradlew
MODIFIED
11#!/bin/sh
22
33#
4# Copyright © 2015-2021 the original authors.
4# Copyright © 2015 the original authors.
55#
66# Licensed under the Apache License, Version 2.0 (the "License");
77# you may not use this file except in compliance with the License.
ios/HelloWorld/AppDelegate.swift
MODIFIED
1import Expo
1internal import Expo
22import React
33import ReactAppDependencyProvider
44
5@UIApplicationMain
6public class AppDelegate: ExpoAppDelegate {
5@main
6class AppDelegate: ExpoAppDelegate {
77 var window: UIWindow?
88
99 var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
1919
2020 reactNativeDelegate = delegate
2121 reactNativeFactory = factory
22 bindReactNativeFactory(factory)
2322
2423#if os(iOS) || os(tvOS)
2524 window = UIWindow(frame: UIScreen.main.bounds)
ios/Podfile
MODIFIED
77def ccache_enabled?(podfile_properties)
88 # Environment variable takes precedence
99 return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE']
10
10
1111 # Fall back to Podfile properties
1212 podfile_properties['apple.ccacheEnabled'] == 'true'
1313end
1414
15ENV['RCT_NEW_ARCH_ENABLED'] ||= '0' if podfile_properties['newArchEnabled'] == 'false'
1615ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
17ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
18ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
16ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true'
17ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true'
18ENV['RCT_HERMES_V1_ENABLED'] ||= '1' if podfile_properties['expo.useHermesV1'] == 'true'
1919platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
2020
2121prepare_react_native_project!
package.json
MODIFIED
22 "name": "expo-template-bare-minimum",
33 "description": "This bare project template includes a minimal setup for using unimodules with React Native.",
44 "license": "0BSD",
5 "version": "54.0.50",
5 "version": "55.0.8",
66 "main": "index.js",
77 "scripts": {
88 "start": "expo start --dev-client",
1111 "web": "expo start --web"
1212 },
1313 "dependencies": {
14 "expo": "~54.0.33",
15 "expo-status-bar": "~3.0.9",
16 "react": "19.1.0",
17 "react-native": "0.81.5"
14 "expo": "~55.0.0-preview.9",
15 "expo-status-bar": "~55.0.2",
16 "react": "19.2.0",
17 "react-native": "0.83.1"
1818 }
1919}
2020