Native project upgrade helper
Edit this 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 53 to 54
64 | 64 | } |
65 | 65 | |
66 | 66 | /** |
67 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode. | |
67 | * Set this to true in release builds to optimize the app using [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization). | |
68 | 68 | */ |
69 | def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean() | |
69 | def enableMinifyInReleaseBuilds = (findProperty('android.enableMinifyInReleaseBuilds') ?: false).toBoolean() | |
70 | 70 | |
71 | 71 | /** |
72 | 72 | * The preferred build flavor of JavaScriptCore (JSC) |
94 | 94 | targetSdkVersion rootProject.ext.targetSdkVersion |
95 | 95 | versionCode 1 |
96 | 96 | versionName "1.0" |
97 | ||
98 | buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\"" | |
97 | 99 | } |
98 | 100 | signingConfigs { |
99 | 101 | debug { |
112 | 114 | // see https://reactnative.dev/docs/signed-apk-android. |
113 | 115 | signingConfig signingConfigs.debug |
114 | 116 | shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false) |
115 | minifyEnabled enableProguardInReleaseBuilds | |
117 | minifyEnabled enableMinifyInReleaseBuilds | |
116 | 118 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" |
117 | 119 | crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true) |
118 | 120 | } |
5 | 5 | |
6 | 6 | import com.facebook.react.PackageList |
7 | 7 | import com.facebook.react.ReactApplication |
8 | import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative | |
8 | 9 | import com.facebook.react.ReactNativeHost |
9 | 10 | import com.facebook.react.ReactPackage |
10 | 11 | import com.facebook.react.ReactHost |
11 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load | |
12 | import com.facebook.react.common.ReleaseLevel | |
13 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint | |
12 | 14 | import com.facebook.react.defaults.DefaultReactNativeHost |
13 | import com.facebook.react.soloader.OpenSourceMergedSoMapping | |
14 | import com.facebook.soloader.SoLoader | |
15 | 15 | |
16 | 16 | import expo.modules.ApplicationLifecycleDispatcher |
17 | 17 | import expo.modules.ReactNativeHostWrapper |
19 | 19 | class MainApplication : Application(), ReactApplication { |
20 | 20 | |
21 | 21 | override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper( |
22 | this, | |
23 | object : DefaultReactNativeHost(this) { | |
24 | override fun getPackages(): List<ReactPackage> { | |
25 | val packages = PackageList(this).packages | |
26 | // Packages that cannot be autolinked yet can be added manually here, for example: | |
27 | // packages.add(MyReactNativePackage()) | |
28 | return packages | |
29 | } | |
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 | } | |
30 | 29 | |
31 | 30 | override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry" |
32 | 31 | |
33 | 32 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG |
34 | 33 | |
35 | 34 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED |
36 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED | |
37 | 35 | } |
38 | 36 | ) |
39 | 37 | |
42 | 40 | |
43 | 41 | override fun onCreate() { |
44 | 42 | super.onCreate() |
45 | SoLoader.init(this, OpenSourceMergedSoMapping) | |
46 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { | |
47 | // If you opted-in for the New Architecture, we load the native entry point for this app. | |
48 | load() | |
43 | try { | |
44 | DefaultNewArchitectureEntryPoint.releaseLevel = ReleaseLevel.valueOf(BuildConfig.REACT_NATIVE_RELEASE_LEVEL.uppercase()) | |
45 | } catch (e: IllegalArgumentException) { | |
46 | DefaultNewArchitectureEntryPoint.releaseLevel = ReleaseLevel.STABLE | |
49 | 47 | } |
48 | loadReactNative(this) | |
50 | 49 | ApplicationLifecycleDispatcher.onApplicationCreate(this) |
51 | 50 | } |
52 | 51 |
15 | 15 | # When configured, Gradle will run in incubating parallel mode. |
16 | 16 | # This option should only be used with decoupled projects. More details, visit |
17 | 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects |
18 | # org.gradle.parallel=true | |
18 | org.gradle.parallel=true | |
19 | 19 | |
20 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the |
21 | 21 | # Android operating system, and which are packaged with your app's APK |
41 | 41 | # If set to false, you will be using JSC instead. |
42 | 42 | hermesEnabled=true |
43 | 43 | |
44 | # Use this property to enable edge-to-edge display support. | |
45 | # This allows your app to draw behind system bars for an immersive UI. | |
46 | # Note: Only works with ReactActivity and should not be used with custom Activity. | |
47 | edgeToEdgeEnabled=true | |
48 | ||
44 | 49 | # Enable GIF support in React Native images (~200 B increase) |
45 | 50 | expo.gif.enabled=true |
46 | 51 | # Enable webp support in React Native images (~85 KB increase) |
1 | 1 | distributionBase=GRADLE_USER_HOME |
2 | 2 | distributionPath=wrapper/dists |
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip | |
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip | |
4 | 4 | networkTimeout=10000 |
5 | 5 | validateDistributionUrl=true |
6 | 6 | zipStoreBase=GRADLE_USER_HOME |
114 | 114 | NONSTOP* ) nonstop=true ;; |
115 | 115 | esac |
116 | 116 | |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | |
117 | CLASSPATH="\\\"\\\"" | |
118 | 118 | |
119 | 119 | |
120 | 120 | # Determine the Java command to use to start the JVM. |
213 | 213 | set -- \ |
214 | 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ |
215 | 215 | -classpath "$CLASSPATH" \ |
216 | org.gradle.wrapper.GradleWrapperMain \ | |
216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ | |
217 | 217 | "$@" |
218 | 218 | |
219 | 219 | # Stop when "xargs" is not available. |
70 | 70 | :execute |
71 | 71 | @rem Setup the command line |
72 | 72 | |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar | |
73 | set CLASSPATH= | |
74 | 74 | |
75 | 75 | |
76 | 76 | @rem Execute Gradle |
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* | |
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* | |
78 | 78 | |
79 | 79 | :end |
80 | 80 | @rem End local scope for the variables with windows NT shell |
168 | 168 | files = ( |
169 | 169 | ); |
170 | 170 | inputPaths = ( |
171 | "$(SRCROOT)/.xcode.env", | |
172 | "$(SRCROOT)/.xcode.env.local", | |
171 | 173 | ); |
172 | 174 | name = "Bundle React Native code and images"; |
173 | 175 | outputPaths = ( |
1 | { | |
2 | "images" : [ | |
3 | { | |
4 | "filename" : "SplashScreenLegacy.png", | |
5 | "idiom" : "universal", | |
6 | "scale" : "1x" | |
7 | }, | |
8 | { | |
9 | "idiom" : "universal", | |
10 | "scale" : "2x" | |
11 | }, | |
12 | { | |
13 | "idiom" : "universal", | |
14 | "scale" : "3x" | |
15 | } | |
16 | ], | |
17 | "info" : { | |
18 | "author" : "xcode", | |
19 | "version" : 1 | |
20 | } | |
21 | } |
6 | 6 | |
7 | 7 | ENV['RCT_NEW_ARCH_ENABLED'] = '0' if podfile_properties['newArchEnabled'] == 'false' |
8 | 8 | ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] |
9 | ||
9 | ENV['RCT_USE_RN_DEP'] = '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false' | |
10 | ENV['RCT_USE_PREBUILT_RNCORE'] = '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false' | |
10 | 11 | platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' |
11 | install! 'cocoapods', | |
12 | :deterministic_uuids => false | |
13 | 12 | |
14 | 13 | prepare_react_native_project! |
15 | 14 | |
49 | 48 | :mac_catalyst_enabled => false, |
50 | 49 | :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', |
51 | 50 | ) |
52 | ||
53 | # This is necessary for Xcode 14, because it signs resource bundles by default | |
54 | # when building for devices. | |
55 | installer.target_installation_results.pod_target_installation_results | |
56 | .each do |pod_name, target_installation_result| | |
57 | target_installation_result.resource_bundle_targets.each do |resource_bundle_target| | |
58 | resource_bundle_target.build_configurations.each do |config| | |
59 | config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' | |
60 | end | |
61 | end | |
62 | end | |
63 | 51 | end |
64 | 52 | end |
2 | 2 | "name": "expo-template-bare-minimum", |
3 | 3 | "description": "This bare project template includes a minimal setup for using unimodules with React Native.", |
4 | 4 | "license": "0BSD", |
5 | "version": "53.0.38", | |
5 | "version": "54.0.1", | |
6 | 6 | "main": "index.js", |
7 | 7 | "scripts": { |
8 | 8 | "start": "expo start --dev-client", |
11 | 11 | "web": "expo start --web" |
12 | 12 | }, |
13 | 13 | "dependencies": { |
14 | "expo": "~53.0.20", | |
15 | "expo-status-bar": "~2.2.3", | |
16 | "react": "19.0.0", | |
17 | "react-native": "0.79.6" | |
18 | }, | |
19 | "devDependencies": { | |
20 | "@babel/core": "^7.20.0" | |
14 | "expo": "~54.0.0-preview.1", | |
15 | "expo-status-bar": "~3.0.1", | |
16 | "react": "19.1.0", | |
17 | "react-native": "0.81.0" | |
21 | 18 | } |
22 | 19 | } |