EAS Update in Expo: How to Deploy JavaScript OTA Releases Safely
Learn how Expo’s EAS Update lets you push JavaScript changes to live apps without a new store build. The guide covers the minimal configuration, runtime‑version safety, and common pitfalls to avoid.
11 Oct 2026, 06:01 UTC

Why EAS Update is Useful
When you need to ship a quick bug fix or tweak UI text, a full app‑store resubmission can take days. Expo’s EAS Update solves this by publishing a JavaScript bundle and assets to Expo’s hosted service. Installed clients download the new bundle on launch, so users see the change instantly—without touching the native layer.
Minimal Configuration
Assume you already have an Expo project with expo-updates installed. Run the following to add the required fields:
npx eas update:configure
This adds an updates.url and a runtimeVersion policy to app.json (or app.config.js). The file now looks like this:
{
"expo": {
"name": "MyApp",
"slug": "my-app",
"version": "1.0.0",
"runtimeVersion": {
"policy": "fingerprintExperimental"
},
"updates": {
"url": "https://u.expo.dev/PROJECT_ID",
"checkAutomatically": "ON_LOAD",
"fallbackToCacheTimeout": 0
}
}
}
Next, create an eas.json build profile that points to your update channel:
{
"cli": {
"version": ">= 3.0.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
}
},
"update": {
"staging": {
"branch": "staging"
},
"production": {
"branch": "production"
}
}
}
Now you can publish a new JavaScript bundle to the production channel with:
eas update --branch production --message "Fix login typo"
Users who open the app after the next launch will see the updated code.
Runtime Version – The Safety Net
The key to avoiding crashes is the runtimeVersion policy. It ensures that a bundle is only delivered to a binary that matches the policy’s fingerprint or app version. The default fingerprintExperimental policy automatically changes the runtime version whenever you add or remove a native module, or upgrade the Expo SDK. This means:
- After a native change, you must rebuild the app and release it to the store.
- Any OTA bundle published before the rebuild will be ignored by the new binary.
- If you forget to bump the runtime version, the bundle will still be delivered, but native API calls that are missing will crash.
To manually set a runtime version, you can use the appVersion policy:
"runtimeVersion": { "policy": "appVersion" }
Then bump expo.version whenever you change native code. This gives you explicit control but requires discipline.
Controlling Update Delivery
By default updates are checked on every app load and applied on the next cold start. You can fine‑tune this with the updates.checkAutomatically and updates.fallbackToCacheTimeout fields in app.json. For example, to check only when the user explicitly requests a refresh:
"checkAutomatically": "NEVER"
Then use the expo-updates API in your code:
import { checkForUpdateAsync, fetchUpdateAsync, reloadAsync } from 'expo-updates';
async function manualRefresh() {
const update = await checkForUpdateAsync();
if (update) {
await fetchUpdateAsync();
await reloadAsync();
}
}
Rollback Strategy
If a published bundle contains a bug, you can roll back by republishing a previous update:
eas update:republish --branch production --message "Rollback to v1.2.3"
Alternatively, point the channel back to an older update ID with eas update --branch production --force --update-id OLD_ID. The client will then download the older bundle on its next check.
Common Mistakes and How to Avoid Them
- Forgetting the Runtime Version: Publishing an update that uses a new native API without bumping
runtimeVersionwill crash on the first launch after the update. Always verify thatexpo.versionor the fingerprint policy matches the binary. - Assuming OTA is for Major Features: App‑store policies allow OTA for bug fixes and UI tweaks only. Adding new functionality that changes the app’s core purpose may violate Apple or Google review guidelines. Keep OTA content minimal.
- Ignoring the Check‑Automatic Setting: If you rely on the default
ON_LOADbehavior, a broken bundle may not surface until the user restarts the app. Test in a staging channel first and consider usingNEVERwith a manual refresh during QA. - Using Self‑Hosted Updates Without Signing: While you can host bundles yourself, you must sign them with a valid key and host the update server. The managed EAS service handles this for you, reducing infrastructure overhead.
- Misunderstanding the Branch Concept: Branches are logical channels (e.g.,
production,staging). Publishing to a wrong branch means the intended users won’t receive the update. Double‑check the branch flag before publishing.
Verifying Your Setup
After configuring, you can quickly confirm the OTA flow:
- Run
npx eas update:configureand inspectapp.jsonforupdates.urlandruntimeVersion. - Build a development client with
eas build --profile developmentand install it on a device. - Publish a trivial change (e.g., change a string) to the
stagingbranch. - Restart the app and verify the change appears.
- Add a native module (e.g.,
expo-sensors), rebuild the app, and bump the runtime version. Publish another update that references the new native API and confirm the app does not crash.
These steps give you confidence that your OTA updates are safe and that the runtime‑version guard is functioning as intended.
When to Use EAS Update
Use EAS Update when:
- You need to ship a quick bug fix or UI tweak.
- Native code changes are not required.
- All users share the same binary version (e.g., after a recent store release).
When you plan to add new native modules or significantly change the app’s functionality, always build a new store version first, bump the runtime version, and then publish any OTA updates that complement the new binary.
Conclusion
EAS Update gives Expo developers a powerful, low‑friction channel to deliver JavaScript changes. By correctly configuring the runtime‑version policy, controlling update delivery, and following the rollback steps, you can ship fixes quickly while protecting users from crashes caused by mismatched native code.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.