| name | mobile-ota-updates |
|---|---|
| description | Deploy over-the-air JavaScript updates to a React Native/Expo app using EAS Update. Covers channels, runtime versions, staged rollouts, rollback, bandwidth management, and testing published updates. For Flutter, covers Shorebird. Use when the user wants to push fixes without a full app store release. |
| standards-version | 1.6.3 |
Use this skill when the user:
- Wants to push bug fixes or content changes without a store release
- Asks about EAS Update, CodePush, or Shorebird
- Needs staged rollouts or rollback for updates
- Wants to manage multiple update channels (production, staging, preview)
- Mentions "OTA", "over-the-air", "eas update", "hot update", "code push", or "shorebird"
- Framework: Expo (React Native) or Flutter
- Channel strategy: single channel or multi-channel (production, staging)
- Runtime version policy: appVersion, nativeVersion, or fingerprint (Expo)
-
Understand what OTA can and cannot update.
Can update (JS/assets) Cannot update (requires new binary) Bug fixes in TypeScript/JavaScript New native modules (e.g. adding expo-camera) UI changes, styling app.json config plugin changes Business logic Expo SDK version upgrades Images, fonts, JSON data Android/iOS permission additions Navigation structure Native code changes (Swift/Kotlin) If the change touches native code, you must submit a new binary through the app stores.
-
Configure EAS Update in app.json. Use
mobile_configureOTAto automate this:{ "expo": { "runtimeVersion": { "policy": "fingerprint" }, "updates": { "url": "https://u.expo.dev/YOUR_PROJECT_ID", "enabled": true, "fallbackToCacheTimeout": 0, "checkAutomatically": "ON_LOAD" } } }Runtime version policies:
fingerprint(recommended): auto-generated hash of native config. Updates are only delivered to compatible binaries.appVersion: usesexpo.version. You manually control compatibility.nativeVersion: usesexpo.ios.buildNumber/expo.android.versionCode.
-
Set up channels in eas.json. Each build profile targets a channel:
{ "build": { "production": { "channel": "production" }, "preview": { "channel": "preview", "distribution": "internal" } } } -
Publish an update. After making a JS-only change:
eas update --channel production --message "Fix checkout crash on Android"For staged rollouts, limit the percentage of users who receive the update:
eas update --channel production --message "New onboarding flow" --rollout-percentage 10Increase the rollout after monitoring:
eas update:rollout --channel production --percentage 50 eas update:rollout --channel production --percentage 100
-
Roll back a bad update. If an update causes issues, republish the previous known-good bundle:
# List recent updates eas update:list --channel production # Rollback to the previous update eas update:rollback --channel production
The rollback takes effect on the next app launch (users must close and reopen).
-
Test updates before production. Publish to a preview channel first:
eas update --channel preview --message "Testing new feature"Open the update in a development build using Expo Orbit or by scanning the QR code from the EAS dashboard. Force-close and reopen the app twice to ensure the update loads.
-
Monitor update adoption. In the EAS dashboard, check:
- How many users are on the latest update
- Error rates per update (combine with Sentry release tracking)
- Bandwidth consumption (users download the full JS bundle on each update)
-
Flutter: Shorebird (alternative). For Flutter apps, Shorebird provides OTA updates:
# Install Shorebird CLI curl --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh -sSf | bash # Initialize in your Flutter project shorebird init # Create a release build shorebird release android shorebird release ios # Push a patch (OTA update) shorebird patch android shorebird patch ios
Shorebird patches Dart code at the AOT level, not JavaScript. Limitations are similar: no native code changes, no new plugins.
- EAS Update: Getting started
- EAS Update: Best practices
- EAS Update: Rollouts
- EAS Update: Runtime versions
- Shorebird: Getting started
User: "I found a crash in production and need to push a fix without going through app review."
Agent:
- Confirms the fix is JS-only (no native code changes)
- Verifies EAS Update is configured with
mobile_configureOTA - Walks through fixing the bug locally
- Publishes to the preview channel for testing:
eas update --channel preview - After testing, publishes to production at 10% rollout:
eas update --channel production --rollout-percentage 10 - Monitors crash rates via Sentry, then increases to 100%
- Explains that users get the fix on next app launch without a store update
| Step | MCP Tool | Description |
|---|---|---|
| Configure OTA | mobile_configureOTA |
Set up EAS Update config in app.json with channels and runtime version |
| Check build health | mobile_checkBuildHealth |
Verify app.json has valid update config |
| Build baseline | mobile_buildForStore |
Create the initial binary that OTA updates will target |
| Analyze impact | mobile_analyzeBundle |
Check bundle size before publishing (affects download bandwidth) |
- Pushing native changes via OTA - If you added a new native module or changed a config plugin, the update will crash on incompatible binaries. The
fingerprintruntime version policy prevents this automatically. - Not testing updates - Always publish to a preview channel first. A broken OTA update affects all users immediately (unlike store releases, which can take days to propagate).
- Ignoring bandwidth - Each OTA update downloads the full JS bundle. For apps with frequent updates and large bundles, this adds up. Use
mobile_analyzeBundleto minimize bundle size. - Forgetting fallbackToCacheTimeout - Setting this to
0means the app loads the cached bundle immediately and downloads updates in the background. Setting it higher blocks launch until the update downloads, which hurts perceived performance. - Runtime version mismatch - If you change the runtime version policy mid-project, existing users may stop receiving updates. Stick with one policy.
- Rollback delay - Rollbacks take effect on the next app launch. Users who have already loaded the bad update will keep it until they restart the app.
- EAS Update requires EAS Build - Updates are tied to builds created with EAS Build. Locally built binaries (expo run:ios) do not receive EAS Updates.
- Mobile Analytics - track crash rates per update release
- Mobile iOS Submission - when OTA is not enough and a store release is needed
- Mobile Android Submission - Play Store releases for native changes