HomeGuidesReferenceLearn
ArchiveExpo SnackDiscord and ForumsNewsletter

Install expo-updates in a bare React Native project

Learn how to install and configure expo-updates in bare React Native projects.


The expo-updates library fetches and manages updates to an app's JavaScript and assets that are received from a remote server. It supports EAS Update, a hosted service that serves updates and provides a variety of tools for managing them. It can also be configured to work with a custom server. This guide explains how to set it up for use with EAS Update.

Do you need to create a new project?

If you are creating a new project, we recommend using the following command:

Terminal
- npx create-expo-app --template bare-minimum
Terminal
- yarn create expo-app --template bare-minimum

Running this command will create a new project from the bare-minimum template that will handle configuring the expo-updates config plugin and the installation steps described in the following sections of this guide.

Do you use Continuous Native Generation (CNG) in your project?

You may be reading the wrong guide then. To use expo-updates in a project that uses CNG, see EAS Update "Get started".

Prerequisites

The expo package must be installed and configured. If you created your project with npx react-native init and do not have any other Expo libraries installed, you will need to install Expo modules before proceeding.

Installation

To get started, install expo-updates:

Terminal
- npx expo install expo-updates

Then, install pods for iOS:

Terminal
- npx pod-install

Configuring expo-updates library

Apply the changes from the diffs from the following sections to configure expo-updates in your project.

JavaScript and JSON

Modify the expo section of app.json. (If you created your project using npx react-native init, you will need to add this section.)

The changes below add the updates URL to the Expo configuration.

The example updates URL and projectId shown below are used with EAS Update. The EAS CLI sets this URL correctly for the EAS Update service when running eas update:configure.

app.json
 {
   "name": "MyApp",
-  "displayName": "MyApp"
+  "displayName": "MyApp",
+  "expo": {
+    "name": "MyApp",
+    "slug": "MyApp",
+    "ios": {
+      "bundleIdentifier": "com.MyApp"
+    },
+    "android": {
+      "package": "com.MyApp"
+    },
+    "runtimeVersion": "1.0.0",
+    "updates": {
+      "url": "https://u.expo.dev/[your-project-id]"
+    },
+    "extra": {
+      "eas": {
+        "projectId": "[your-project-id]"
+      }
+    }
+  }
 }

Run eas update:configure to set the updates URL and projectId in app.json.

Terminal
- eas update:configure

If you want to set up a custom server instead, add your URL to updates.url in app.json.

app.json
 {
   "name": "MyApp",
   "displayName": "MyApp",
   "expo": {
     "name": "MyApp",
      ...
     "updates": {
-      "url": "https://u.expo.dev/[your-project-id]"
+      "url": "http://localhost:3000/api/manifest"
     }
   }
 }

Android

Modify android/app/build.gradle to check for the JS engine configuration (JSC or Hermes) in Expo files:

android/app/build.gradle
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -52,6 +52,11 @@ react {
     // hermesFlags = ["-O", "-output-source-map"]
 }

+// Override `hermesEnabled` by `expo.jsEngine`
+ext {
+  hermesEnabled = (findProperty('expo.jsEngine') ?: "hermes") == "hermes"
+}
+
 /**
  * Set this to true to create four separate APKs instead of one,
  * one for each native architecture. This is useful if you don't

Modify android/app/src/main/AndroidManifest.xml to add the expo-updates configuration XML so that it matches the contents of app.json:

android/app/src/main/AndroidManifest.xml
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -9,6 +9,11 @@
       android:roundIcon="@mipmap/ic_launcher_round"
       android:allowBackup="false"
       android:theme="@style/AppTheme">
+      <meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
+      <meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
+      <meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
+      <meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
+      <meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="http://localhost:3000/api/manifest"/>
       <activity
         android:name=".MainActivity"
         android:label="@string/app_name"

If using the updates server URL shown above (a custom non-HTTPS update server running on the same machine), you will need to modify android/app/src/main/AndroidManifest.xml as shown below:

android/app/src/main/AndroidManifest.xml
 <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:usesCleartextTraffic="true"
 >

Add the Expo runtime version string key to android/app/src/main/res/values/strings.xml:

iOS

Add the file Podfile.properties.json to the ios directory:

ios/Podfile.properties.json
{
  "expo.jsEngine": "hermes"
}

Modify ios/Podfile to check for the JS engine configuration (JSC or Hermes) in Expo files:

ios/Podfile
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -2,6 +2,9 @@ require File.join(File.dirname(`node --print "require.resolve('expo/package.json
 require_relative '../node_modules/react-native/scripts/react_native_pods'
 require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

+require 'json'
+podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
+
 platform :ios, '13.0'
 prepare_react_native_project!

@@ -41,7 +44,7 @@ target 'MyApp' do
     # Hermes is now enabled by default. Disable by setting this flag to false.
     # Upcoming versions of React Native may rely on get_default_flags(), but
     # we make it explicit here to aid in the React Native upgrade process.
-    :hermes_enabled => flags[:hermes_enabled],
+    :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
     :fabric_enabled => flags[:fabric_enabled],
     # Enables Flipper.
     #

Add the Supporting directory containing Expo.plist to your project in Xcode with the following content, to match the content in app.json:

Expo.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>EXUpdatesCheckOnLaunch</key>
    <string>ALWAYS</string>
    <key>EXUpdatesEnabled</key>
    <true/>
    <key>EXUpdatesLaunchWaitMs</key>
    <integer>0</integer>
    <key>EXUpdatesRuntimeVersion</key>
    <string>1.0.0</string>
    <key>EXUpdatesURL</key>
    <string>http://localhost:3000/api/manifest</string>
  </dict>
</plist>

Next steps