Bypassing the App Store: Implementing OTA Updates with expo-updates
Learn how to use expo-updates to deploy JavaScript fixes and features instantly, bypassing the App Store review process while maintaining native stability.
31 May 2026, 18:26 UTC

The Deployment Bottleneck
The standard mobile release cycle is slow. Even a one-line typo fix in a React Native component usually requires a full native build, a submission to Apple or Google, and a review period that can stretch from hours to days. For teams that need to push critical bug fixes or iterate on UI logic quickly, that latency is a real engineering cost.
The fix is Over-the-Air (OTA) updates. With the expo-updates library, you can ship a new JavaScript bundle straight to installed devices, skipping store review entirely for any change that does not touch the native layer.
How OTA Updates Function
An Expo app has two parts: the native binary (the "shell") and the JavaScript bundle (the "logic"). When the app starts, expo-updates checks a remote server—defined by the updates.url field in your app config—to see whether a newer bundle exists for the app's runtime version.
If a newer bundle is found, the app downloads it and applies it on the next launch. No store visit is involved. The hard limit: OTA updates cannot change native code. Adding a native library or altering config that affects the binary still requires a full rebuild and store submission.
Configuring the Update Strategy
Expo Go, the sandbox app, fetches bundles from Expo's servers automatically. Production apps built as development builds need explicit configuration in app.json or app.config.js:
// app.json
{
"expo": {
"updates": {
"url": "https://u.expo.dev/[your-project-id]",
"enabled": true,
"checkAutomatically": "ON_LOAD"
},
"runtimeVersion": {
"policy": "appVersion"
}
}
}
The runtimeVersion is the key safety mechanism. With the appVersion policy, a bundle is only delivered to binaries reporting a matching runtime version. That prevents a bundle compiled against version 1.1.0's native modules from being loaded into a 1.0.0 binary and crashing on a missing module. Replace [your-project-id] with the project ID shown on your Expo dashboard.
Worked Example: Pushing a Hotfix
Suppose version 1.0.0 of your production app has a logic error in the checkout screen. Rather than a store submission:
- Fix the bug in
CheckoutScreen.js. - Publish from your project root with an authenticated
eas-cliinstall:eas update --branch production --message "Fix checkout logic error". The CLI bundles the JavaScript, reads the runtime version from app config, and uploads the update to the production branch. - Confirm on the EAS dashboard that the production branch now lists a new update with a new bundle hash.
Expected result: on each user's next launch, expo-updates detects the new hash, downloads the bundle in the background, and applies it on the following restart. The fix lands one launch after detection—not the moment you publish.
Trade-offs and Limitations
- Broken update delivery: if
updates.urlis wrong or the server is unreachable, the app falls back to the embedded or cached bundle. A bad update that does load can white-screen users until you publish a correction they can fetch—or ship a store release as the last resort. - Platform behavior differs: iOS commonly requires a full app restart before a downloaded bundle runs, so do not promise instant hot-swap behavior in UX copy.
- Store policy: Apple and Google permit OTA updates for bug fixes and content, but using them to change what the app fundamentally does can lead to rejection or removal.
Verifying the Pipeline Before You Need It
Rehearse the flow on a development build, not in production:
- Build locally with
npx expo run:iosornpx expo run:android(requires Xcode or the Android SDK on the machine). - Launch the app and watch the console and network logs for requests to your configured update URL.
- Push a trivial change, such as a text color, with
eas updateto the branch the build uses. - Fully restart the app. If the change appears without a new native build, the OTA path is wired correctly.
Do this rehearsal before your first real incident. A hotfix path you have never exercised is not a hotfix path; it is a hypothesis.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.