OTA Updates Basics
10 examples to get you started with Over-The-Air (OTA) updates on Expo SDK 57 - what EAS Update can change in minutes versus what still requires an App Store or Play Store submission.
Search across all documentation pages
10 examples to get you started with Over-The-Air (OTA) updates on Expo SDK 57 - what EAS Update can change in minutes versus what still requires an App Store or Play Store submission.
OTA updates require expo-updates in a release or preview binary - not Expo Go.
npx create-expo-app@latest MyOtaApp --template blank-typescript
cd MyOtaApp
npx expo install expo-updates
npx eas init
npx eas update:configureTooling: These examples target Expo SDK 57 (
expo~57.0.4), React Native 0.86.0, and React 19.2.3.
Every mobile release decision starts with one question: does this change touch native code?
JavaScript / assets change only → eas update (minutes)
Native shell / permissions / SDK → eas build + store submit (days)Related: expo-updates (Client API) - checking and applying updates from app code
Pure JavaScript logic errors are the classic OTA use case - no native boundary crossed.
// src/screens/CheckoutScreen.tsx - typo fix ships OTA
export function CheckoutScreen() {
const total = useCartTotal();
return (
<View>
<Text>Total: {formatCurrency(total)}</Text>
{/* Fixed: was dividing by 100 twice */}
<PrimaryButton label="Pay now" onPress={submitPayment} />
</View>
);
}# After preview soak
eas update --channel production --environment production --message "Fix checkout total display"preview first; soak before production on revenue pathsRelated: Release Channels & Branches - staging → production promotion
Bundled images, fonts referenced from JS, and Lottie JSON ship with the update manifest.
// src/components/HeroBanner.tsx
import heroImage from "@/assets/hero-summer.png";
export function HeroBanner() {
return <Image source={heroImage} style={{ width: "100%", height: 200 }} />;
}Installing a package with native code changes ios/ and android/ output - OTA cannot add it.
# This sequence requires eas build + store submit, NOT eas update alone
npx expo install react-native-vision-camera// app.config.ts - new plugin = native rebuild
export default {
expo: {
plugins: [
[
"react-native-vision-camera",
{ cameraPermissionText: "We need camera access for barcode scan." },
],
],
},
};npx expo prebuild diff is the signal - if native project changes, plan a store buildRelated: EAS Build Basics - cloud binaries that receive updates
Permission copy lives in native manifests - users see stale text if you OTA without rebuilding.
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "Scan barcodes to add items to your cart."
}
},
"android": {
"permissions": ["android.permission.CAMERA"]
}
}
}infoPlist, permissions, or entitlements change needs eas buildSDK upgrades change the native runtime, Hermes version, and default native dependencies.
{
"dependencies": {
"expo": "~57.0.4",
"react": "19.2.3",
"react-native": "0.86.0"
}
}eas build for all profiles, then resume eas update on the new runtime lineRelated: Runtime Version Policy -
appVersion,nativeVersion, and fingerprint strategies
runtimeVersion tells EAS Update which JS bundles a binary may download. Mismatch = silent skip.
// app.config.ts
export default {
expo: {
version: "2.4.0",
runtimeVersion: {
policy: "appVersion",
},
updates: {
url: "https://u.expo.dev/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
},
},
};# Build embeds runtime 2.4.0
eas build --profile production --platform all
# Update must target the same runtime
eas update --channel production --environment productioneas build artifacts and eas update bundles must share the same resolved runtimeVersionnpx expo config --type public | jq '.runtimeVersion'Related: expo-updates Configuration - full
app.configcookbook
Copy this table into your release runbook and require a checkbox per change.
| Change | OTA? | Store? | Notes |
|---|---|---|---|
| JS bug fix | ✅ | - | Preview soak first |
| New React screen (no new native) | ✅ | - | Watch bundle size |
New expo install native module | - | ✅ | Bump runtime; gate JS |
| Config plugin added/changed | - | ✅ | Prebuild diff required |
| App icon / adaptive icon | - | ✅ | Native asset pipeline |
| Push notification icon (Android) | - | ✅ | Native resource |
| Remote copy / theme tokens in JS | ✅ | - | No native touch |
| SQLite schema migration in JS | ✅ | ⚠️ | Test upgrade path; bad migration bricks |
| API breaking change | ✅ | ⚠️ | Old JS must tolerate old API |
| Hermes bytecode / bundle size | ✅ | - | Treat as release event |
OTA lets you ship JS fast; feature flags let you control who executes it.
// src/features/barcode/useBarcodeScanner.ts
import * as Application from "expo-application";
const MIN_NATIVE_FOR_SCANNER = "2.4.0";
export function useBarcodeScannerEnabled(remoteFlag: boolean) {
const nativeVersion = Application.nativeApplicationVersion ?? "0.0.0";
const nativeReady = compareSemver(nativeVersion, MIN_NATIVE_FOR_SCANNER) >= 0;
return remoteFlag && nativeReady;
}Application.nativeApplicationVersion reflects the store binary, not the OTA bundle IDRelated: Feature Flags for Safe Rollout - kill switches alongside OTA
Support and incident response need the update ID - not just the marketing version.
// app/_layout.tsx (dev/preview diagnostics only)
import * as Updates from "expo-updates";
import { useEffect } from "react";
export default function RootLayout() {
useEffect(() => {
if (__DEV__) return;
console.info("[updates]", {
updateId: Updates.updateId,
runtimeVersion: Updates.runtimeVersion,
channel: Updates.channel,
isEmbeddedLaunch: Updates.isEmbeddedLaunch,
});
}, []);
return <Stack />;
}Updates.updateId identifies the OTA bundle; Updates.isEmbeddedLaunch true means no OTA applied yetNo. OTA replaces JavaScript bundles and downloadable assets. Native modules, permissions, entitlements, SDK version, and app icons require eas build and store submission.
No. Expo Go uses a generic native shell without your expo-updates channel configuration. Test OTA on preview or production profile builds from EAS.
After publish, devices check on launch (per your checkAutomatically policy). Users may need one restart. Critical fixes can prompt reload via expo-updates (Client API).
The app crashes at import or first native call - often immediately on launch. Roll back the channel and ship a store build. Prevention: preview soak + runtime guards + Release & OTA Rules.
They receive OTAs for their runtimeVersion line only. Track runtime distribution weekly; enforce minimum native version when security requires it.
runtimeVersion, URL, check-on-launchStack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026