React Native Builds, Releases & OTA Updates
Shipping a React Native app is different from deploying a website. Native binaries must be built and signed for each platform, reviewed by Apple and Google, and installed by users who may not update for months. At the same time, React Native apps have an unusual superpower: most of their code is a JavaScript bundle, which can be updated over the air (OTA) without a store release, within the stores' rules.
A mature release process combines reproducible cloud or CI builds, automated signing, store submission, OTA updates for JavaScript-only fixes, staged rollouts, and crash and performance monitoring, so teams can ship frequently and roll back quickly.
TL;DR
- Development builds (with dev tools) for daily work; release builds (optimized, signed) for testing and the stores.
- EAS Build (Expo Application Services) builds iOS and Android in the cloud, and manages credentials. Local Xcode and Gradle builds also work.
- Signing: iOS certificates and provisioning profiles; Android upload keystore plus Play App Signing.
- Submit with EAS Submit or fastlane to App Store Connect (TestFlight) and Google Play (internal testing, then production).
- OTA updates (EAS Update, or self-hosted expo-updates servers) ship JS and asset changes instantly, and must match a compatible runtime version.
- Use staged rollouts, crash reporting (Sentry, Crashlytics), and a rollback plan for both store builds and OTA updates.
Quick Example
An Expo project configuration with build profiles and update channels:
Core Concepts
Build Types
Release builds bundle Hermes bytecode, strip dev tools, and enable optimizations like R8/ProGuard on Android. Always test release builds before shipping, since behavior and performance differ from development.
Building: EAS Build vs Local
- EAS Build runs builds on managed macOS and Linux machines, handles signing credentials, caches dependencies, and integrates with CI. It works for Expo and bare React Native projects.
- Local builds use Xcode (
xcodebuild, archives) and Gradle (./gradlew bundleRelease), with CI on macOS runners for iOS. Tools like fastlane automate signing, building, and uploading.
Code Signing
- iOS: an Apple Developer account, distribution certificates, provisioning profiles, and entitlements (push notifications, associated domains). EAS or fastlane
matchmanage these for teams. - Android: an upload key signs app bundles (AAB), and Play App Signing holds the app signing key. Back up the upload keystore (Google can reset it through support, but it's disruptive), and never commit it. See secrets management.
Store Submission
- Apple: upload to App Store Connect, distribute via TestFlight, then submit for review with metadata, screenshots, privacy details (App Privacy labels), and account-deletion requirements where applicable.
- Google Play: upload AABs to testing tracks (internal, closed, open), then production with staged rollout percentages. Complete Data Safety declarations and target API level requirements.
- Review timelines vary. Build release processes that don't depend on same-day approvals.
Configuration and Environments
- Separate API endpoints, feature flags, and keys per environment via build profiles and environment variables (
EXPO_PUBLIC_*for values embedded in the JS bundle). - Anything in the app bundle is public: never embed secrets. Use your backend for privileged operations.
- Separate bundle identifiers or package names (for example
com.acme.app.staging) let staging and production apps coexist on devices.
Over-the-Air Updates
OTA updates download a new JavaScript bundle and assets at launch (or in the background) and apply them on next start:
- EAS Update (hosted) or self-hosted servers speak the expo-updates protocol. CodePush was part of Microsoft App Center, which was retired in 2025, so teams migrated to EAS Update or self-hosted alternatives.
- Runtime version: an update is only compatible with builds that share the same native code. A native change (new module, SDK upgrade, permission) requires a new store build. The
fingerprintpolicy computes runtime versions from native project state to prevent mismatches. - Channels and branches route updates to specific builds (preview vs production), with gradual rollouts and rollback.
- Store policies: Apple and Google allow interpreted code updates that don't change the app's primary purpose or bypass review for new features. Use OTA for fixes and incremental improvements, not to ship an entirely different app.
Versioning
- Version (
2.4.0): user-facing, semver-like. - Build number (iOS
CFBundleVersion, AndroidversionCode): must increase with every store upload. Automate it with EASautoIncrementor CI. - Track the runtime version separately for OTA compatibility.
Monitoring and Rollback
- Crash reporting with symbolication and source maps (Sentry, Firebase Crashlytics, Bugsnag). Upload dSYMs, ProGuard mappings, and JS source maps for every build and update.
- Staged rollouts on Google Play, phased releases on the App Store, and gradual OTA rollouts limit the blast radius.
- Rollback: halt store rollouts, and republish or roll back OTA updates. Remember that users already on a bad native build stay there until they update. See deployment strategies.
Best Practices
Automate the Whole Pipeline
Build, sign, test, submit, and publish updates from CI (GitHub Actions plus EAS or fastlane) on tags or merges. Manual release steps on one engineer's laptop don't scale, and get forgotten.
Separate Native Changes From JS Changes
Know which changes require a new binary (native dependencies, permissions, SDK upgrades) and which can ship OTA. Fingerprint-based runtime versions enforce it automatically.
Keep a Kill Switch and Force-Update Path
Remote config or feature flags let you disable broken features instantly. A minimum-supported-version check prompts users on dangerously old builds to update.
Test Release Builds on Real Devices
Release-only issues (minification, Hermes bytecode, missing permissions, different network security settings) only appear in release builds. Test them before submission, including upgrade paths from the previous version.
Common Mistakes
Shipping an OTA Update With Native Changes
Publishing a JS bundle that expects a new native module to builds that don't have it crashes the app on launch. Match runtime versions, and ship native changes through the stores.
Embedding Secrets in the App
API secrets in environment variables compiled into the bundle are trivially extractable from the binary. Keep secrets on servers, and use short-lived, user-scoped tokens in the app.
No Source Maps for Crash Reports
Without uploaded source maps and debug symbols, production stack traces are unreadable minified code. Upload them automatically with every build and update.
FAQ
What is EAS Build?
Expo Application Services' cloud build service for React Native apps. It builds iOS and Android binaries on managed infrastructure, manages signing credentials, caches dependencies, and integrates with submission and OTA updates. It works for both Expo-managed and bare React Native projects.
Are OTA updates allowed by Apple and Google?
Yes, within policy. Both allow downloading interpreted code (JavaScript) that doesn't significantly change the app's purpose or introduce features circumventing review. Use OTA for bug fixes and incremental improvements, and ship significant new functionality through store releases.
When do I need a new store build instead of an OTA update?
Whenever native code or configuration changes: adding or upgrading native libraries, changing permissions or entitlements, upgrading React Native or the Expo SDK, or modifying app icons, splash screens, or native settings. JavaScript and asset-only changes can go OTA.
How do I roll back a bad release?
For OTA updates, republish the previous update or use the rollback command. It reaches users on next launch. For store builds, halt the staged rollout and submit a fixed build. Users who already installed the bad version need an update, so design with remote kill switches and minimum-version checks.
Related Topics
- React Native — The framework overview
- Expo — EAS Build, Submit, and Update
- CI/CD — Automating release pipelines
- Deployment Strategies — Staged rollouts and rollbacks
- Feature Flags — Remote kill switches
- Push Notifications — Native capabilities requiring store builds