expo-updates library enables remote app code updates
expo-updates is a library that enables your app to manage remote updates to your application code. It communicates with a configured remote update service to get information about available updates. It works with EAS Update, a hosted remote update service that simplifies installation and configuration.
Prerequisites for installing expo-updates in bare React Native projects
If you created your project with npx @react-native-community/cli@latest init and do not have any other Expo libraries installed, you must install Expo modules before proceeding with expo-updates installation.
Installation commands for expo-updates
To install expo-updates, run one of the following commands depending on your package manager: npm: npx expo install expo-updates, yarn: yarn expo install expo-updates, pnpm: pnpm expo install expo-updates, bun: bun expo install expo-updates. After installation, run pod-install: npm: npx pod-install, yarn: yarn dlx pod-install, pnpm: pnpm dlx pod-install, bun: bunx pod-install.
Configuration command for expo-updates
Run eas update:configure to automatically set the updates URL and projectId in app.json. This command correctly configures the URL for the EAS Update service.
Custom expo-updates server URL configuration
To use a custom expo-updates server instead of EAS Update, add the server URL to the updates.url field in app.json. For example, for a local server: http://localhost:3000/api/manifest.
Android build.gradle modification for expo-updates
Modify android/app/build.gradle to check for the JS engine configuration (JSC or Hermes) in Expo files. This is required for proper expo-updates integration.
Android AndroidManifest.xml modification for expo-updates
Modify android/app/src/main/AndroidManifest.xml to add the expo-updates configuration XML so that it matches the contents of app.json.
Android cleartext traffic configuration for non-HTTPS update servers
If using a custom non-HTTPS update server running on the same machine, modify android/app/src/main/AndroidManifest.xml to add the update server URL and enable usesCleartextTraffic.
Android runtime version string key configuration
Add the Expo runtime version string key to android/app/src/main/res/values/strings.xml as part of expo-updates configuration.
iOS Podfile.properties.json configuration
Add ios/Podfile.properties.json file to the ios directory with the content: {"expo.jsEngine": "hermes"}. This specifies the JavaScript engine configuration.
iOS Podfile modification for expo-updates
Modify ios/Podfile to check for the JS engine configuration (JSC or Hermes) in Expo files.
iOS Expo.plist configuration file
Using Xcode, add Expo.plist file to ios/your-project/Supporting directory with the following keys and values: EXUpdatesCheckOnLaunch (string): ALWAYS, EXUpdatesEnabled (boolean): true, EXUpdatesLaunchWaitMs (integer): 0, EXUpdatesRuntimeVersion (string): 1.0.0, EXUpdatesURL (string): the updates server URL (e.g., http://localhost:3000/api/manifest for custom server or https://u.expo.dev/[your-project-id] for EAS Update).
CNG project different guide for expo-updates
If your project uses Continuous Native Generation (CNG), refer to the EAS Update Get started guide instead of this bare React Native installation guide.
Monthly active user definition
A monthly active user (MAU) is a unique user of an app that downloads at least one update via EAS Update within a single monthly billing period.
eas update:configure command setup
Run 'eas update:configure' to set up over-the-air updates in your Expo project. After the command completes, you need to make new builds before sending updates.
Send an update to production channel
To send an over-the-air update, run 'eas update --channel production'. This creates an update and makes it available to builds configured to receive updates on the production channel. The channel is defined in eas.json.
Verify over-the-air update application
To verify an update has been applied, force close the app and reopen it two times. The update should be applied on the second launch.
Automatic updates with EAS Workflows
You can automatically send updates using EAS Workflows. Create a file named .eas/workflows/send-updates.yml at the root of your project with a workflow configuration that specifies the update type and channel parameter. The workflow will run on specified triggers such as pushes to branches.
Example EAS workflow for sending updates
name: Send updates
on:
push:
branches: ['main']
jobs:
send_updates:
name: Send updates
type: update
params:
channel: production
This workflow sends an over-the-air update for the production channel on every commit to the main branch. Run it manually with 'eas workflow:run send-updates.yml'.
Run EAS workflow manually
To manually run an EAS workflow, use the command 'eas workflow:run send-updates.yml' where send-updates.yml is the workflow file name.
EAS Update environment variable inlining
EAS Update uses Metro Bundler in your local environment or CI to build your app bundle, so it will use available .env files to inline EXPO_PUBLIC_ variables into your code.
Hermes bytecode format changes between Hermes versions
The Hermes bytecode format may change between different Hermes versions. An update produced for a specific version of Hermes will not run on a different version of Hermes. Starting from Expo SDK 46, Hermes is bundled within React Native. When updating the React Native version or Hermes version, you should update the runtimeVersion in app.json to prevent app crashes on launch caused by bytecode incompatibility.
Publishing updates generates Hermes bytecode bundles
Publishing updates with eas update and npx expo export will generate Hermes bytecode bundles and their source maps.
Sentry source map upload for EAS Update
After running `eas update`, upload source maps to Sentry by running `npx sentry-expo-upload-sourcemaps dist` (or with yarn dlx, pnpm dlx, bunx). This passes the 'dist' directory generated by `eas update` to the upload script so errors will be properly symbolicated in Sentry.
Chain EAS Update and Sentry sourcemap upload
You can chain commands together with `&&` to publish an update and upload sourcemaps in one step: `eas update --branch <branch> && npx sentry-expo-upload-sourcemaps dist`.
Sentry update metadata tagging for Expo
Add Sentry scope tagging early in your app's lifecycle using `Sentry.getGlobalScope().setTag()` to tag errors with update-related metadata. Set tags for 'expo-update-id' with `Updates.updateId`, 'expo-is-embedded-update' with `Updates.isEmbeddedLaunch`, and optionally 'expo-update-group-id' and 'expo-update-debug-url'. This allows you to see errors happening on certain updates in the Sentry dashboard.
Expo Updates v1 protocol overview
Expo Updates is a protocol for delivering updates to Expo apps running on multiple platforms. An update is defined as a manifest together with the assets referenced inside the manifest. A directive is a message from the server that instructs clients to perform an action. The primary audiences of this spec are Expo Application Services and organizations that wish to manage their own update server.
Client must load most recent update from database
An app running a conformant Expo Updates client library must load the most recent update saved in the client library's update database, possibly after filtering by the contents of the update's manifest metadata.
Client request headers required
A conformant client library must make a GET request with these headers: expo-protocol-version: 1 (to specify version 1 of the Expo Updates specification), expo-platform (to specify the platform type the client is running on; iOS must be 'ios', Android must be 'android'; if not one of these platforms, the server should return 400 or 404), expo-runtime-version (must be a runtime version compatible with the client; stipulates the native code setup a client is running and should be set when the client is built), and any headers stipulated by a previous response's server defined headers.
Client accept header preference
A conformant client library may send one of 'accept: application/expo+json', 'accept: application/json', or 'accept: multipart/mixed' based on supported response structures, though it should send 'accept: application/expo+json, application/json, multipart/mixed'. A conformant client library may express preference using q parameters as specified in RFC 7231, which default to 1.
Client code signing expectation header
A conformant client library configured to perform code signing verification must send an 'expo-expect-signature' header to indicate that it expects the conformant server to include the 'expo-signature' header in the manifest response. 'expo-expect-signature' is an Expo SFV dictionary which may contain: 'sig' (should contain the boolean true to indicate that it requires a conformant server to respond with the signature in the sig key), 'keyid' (should contain the keyId of the public key the client will use to verify the signature), and 'alg' (should contain the algorithm the client will use to verify the signature).
Server response structures supported
A conformant server must return a response structured in at least one of two following response structures: (1) For content-type application/json or application/expo+json, the common response headers and other response headers must be sent in response headers and the manifest body must be sent in response body. This format does not support multiple response parts and therefore does not support directives, and should respond with HTTP 406 error status when the most recent response to be served is not an update. (2) For content-type multipart/mixed, the response must be structured as specified in the multipart response section. A multipart response with no parts may respond with HTTP 204 status and no content.
Server response header selection logic
The choice of update and headers are dependent on the values of the request headers. A conformant server must respond with the most recent update, ordered by creation time, satisfying all parameters and constraints imposed by the request headers. The server may use any properties of the request like its headers and source IP address to choose among several updates that all satisfy the request's constraints.
Common response headers specification
Common response headers must include: expo-protocol-version (describes version of protocol, must be 1), expo-sfv-version (must be 0), expo-manifest-filters (Expo SFV dictionary used to filter updates stored by client library by the metadata attribute found in manifest; if field is mentioned in filter, corresponding field in metadata must either be missing or equal for update to be included; client library must store manifest filters until overwritten by newer response), expo-server-defined-headers (Expo SFV dictionary defining headers that client library must store until overwritten by newer dictionary, and they must be included in every subsequent update request), cache-control (must be set to appropriately short period of time; value of 'cache-control: private, max-age=0' is recommended to ensure newest manifest is returned; setting longer cache ages could result in stale updates), and content-type (must be determined by proactive negotiation as defined in RFC 7231; will always be either application/expo+json or application/json since client library is required to send accept header with each manifest request).
Other response headers specification
Other response headers include: expo-signature (should contain the signature of the manifest to be used during validation step of code signing if the request for the manifest contained the expo-expect-signature header). expo-signature is an Expo SFV dictionary which may contain: 'sig' (must contain the signature of the manifest), 'keyid' (may contain the keyId of the key the server used to sign the response; client should use the certificate that matches this keyid to verify the signature), and 'alg' (may contain the algorithm the server used to sign the response; client should use this field only if it matches the algorithm defined for the certificate matching keyid).
Multipart response part structure
A multipart/mixed response per RFC 2046 may contain three optional parts in any order: (1) 'manifest' part with content-disposition 'form-data; name="manifest"' (name parameter must be 'manifest'), content-type application/json or application/expo+json, optional expo-signature header if code signing is used, and manifest body in part body; (2) 'extensions' part with content-disposition 'form-data; name="extensions"' (name parameter must be 'extensions'), content-type application/json, and extensions-body in part body; (3) 'directive' part with content-disposition 'form-data; name="directive"' (name parameter must be 'directive'), content-type application/json or application/expo+json, optional expo-signature header if code signing is used, and directive body in part body. A multipart response with no parts should be considered a no-op, though headers should be sent and processed by client.
Manifest body TypeScript definition and fields
Manifest is defined as: type Manifest = { id: string; createdAt: string; runtimeVersion: string; launchAsset: Asset; assets: Asset[]; metadata: { [key: string]: string }; extra: { [key: string]: any }; }. Field descriptions: id must uniquely specify the manifest and must be a UUID. createdAt is the date and time at which the update was created (essential as client library selects most recent update subject to constraints from expo-manifest-filters header); datetime should be formatted according to ISO 8601. runtimeVersion can be any string defined by developer; stipulates what native code setup is required to run associated update. launchAsset is a special asset that is the entry point of application code; fileExtension field will be ignored for this asset and should be omitted. assets is an array of assets used by update bundle such as JavaScript, pictures, and fonts; all assets including launchAsset should be downloaded to disk before executing update, and mapping of asset keys to locations on disk should be provided to application code. metadata is string-valued dictionary associated with update; server may send back anything it wishes to be used for filtering updates; metadata must pass filter defined in accompanying expo-manifest-filters header. extra is for storage of optional extra information such as third-party configuration.
Asset object TypeScript definition and fields
Asset is defined as: type Asset = { hash?: string; key: string; contentType: string; fileExtension?: string; url: string; }. Field descriptions: hash is optional base64URL-encoded SHA-256 hash of file to guarantee integrity (base64URL encoding defined by IETF RFC 4648 section 5). key is used to reference this asset from update's application code; may be generated by separate build step that processes application code such as bundler. contentType is MIME type of file as defined by RFC 2045; examples include application/javascript and image/jpeg. fileExtension is optional suggested extension to use when file is saved on client; some platforms such as iOS require certain file types to be saved with extension; extension must be prefixed with a dot; for example .jpeg; in some cases such as launchAsset this field will be ignored in favor of locally determined extension; if field is omitted and there is no locally stipulated extension, asset will be saved without extension; conforming client should prefix file extension with dot if file extension is not empty and missing dot prefix. url is location at which file may be fetched.
Extensions body TypeScript definition
Extensions is defined as: type Extensions = { assetRequestHeaders: ExpoAssetHeaderDictionary; ... }. type ExpoAssetHeaderDictionary = { [assetKey: string]: { [headerName: string]: string, }; }. assetRequestHeaders may contain a dictionary of header key-value pairs to include with asset requests; key and value must both be strings.
Directive body TypeScript definition
Directive is defined as: type Directive = { type: string; parameters?: { [key: string]: any }; extra?: { [key: string]: any }; }. type is the type of directive. parameters may contain any extra information specific to the type. extra is for storage of optional extra information such as third-party information. A conformant client library and server may specify and implement directive types specific to needs of application. For example, Expo Application Services uses 'rollBackToEmbedded' type, which directs expo-updates library to use update embedded in host application instead of any other downloaded updates.
Asset request requirements
A conformant client library must make a GET request to asset URLs specified by manifest. Client library should include a header accepting the asset's content type as specified in manifest. Additionally, client library should specify compression encoding the client library is capable of handling. Client library must also include any header key-value pairs included in assetRequestHeaders for this asset key.
Asset immutability requirement
An asset located at a particular URL must not be changed or removed since client libraries may fetch assets for any update at any time. A conformant client must verify that the base64url-encoded SHA-256 hash of the asset matches the hash field for the asset from the manifest.
Asset response header requirements
Asset must be encoded using compression format that client supports according to request's accept-encoding header; server may serve uncompressed assets. Response must include content-type header with MIME type of asset. Asset is recommended to be served with cache-control header set to long duration since asset at given URL must not change; for example 'cache-control: public, max-age=31536000, immutable'.
Asset compression support
Assets should be capable of being served with Gzip and Brotli compression.
Code signing support in Expo Updates
Expo Updates supports code signing the manifest and directive bodies. Code signing the manifest also transitively signs assets since their hashes are present in manifest and verified by conformant client. A conformant client may request manifest or directive be signed using private key, and then must verify signature of manifest or directive using corresponding code signing certificate before it is used or any corresponding manifest assets are downloaded. Client must verify that signing certificate is either self-signed trusted root certificate or is in certificate chain signed by trusted root certificate; in either case, root certificate must be embedded in application or device's operating system.