
Expo, EAS and Over-the-Air Updates: Shipping React Native Without the Store Queue
The slowest part of mobile development has never been writing the code. It is the distance between finishing a fix and having it on a user's phone: a native build, a submission, a review queue, and then the long tail of users who simply never update. For a copy change or a crash fix that arithmetic is painful, and it is why over-the-air updates went from a niche trick to a standard part of a React Native delivery pipeline.
The consolidation of tooling has made this easier to reason about. With App Center's retirement, the community's centre of gravity moved decisively to Expo's build and update services, and the modern Expo workflow no longer means giving up native modules. Development builds replaced the old sandbox app, and continuous native generation lets you keep the native projects out of your repository and describe the native configuration you need in code instead. Most teams that once ejected on principle now find the managed path fits, and the ones with genuinely unusual native requirements can still use the same build service on a bare project.
The essential thing to understand about OTA updates is the boundary. You can replace the JavaScript bundle and the assets it references. You cannot change native code, add a permission, upgrade the React Native version or swap a native dependency — those require a new binary through the store. This boundary is enforced by a runtime version: an update is only delivered to a build whose native runtime matches it. Teams that treat OTA as a general release channel eventually ship an update that expects a native module the installed binary does not have, and the result is a crash on launch for exactly the users they were trying to help.
What over-the-air updates are genuinely good for :
- Crash and logic fixes in JavaScript, delivered in hours rather than after a review cycle
- Copy, pricing, labels and legal text that must be correct today
- Configuration and feature flag defaults for an audience that has stopped updating
- Staged rollouts — a percentage of users first, so a bad bundle reaches a fraction of your base
- Separate channels for internal, beta and production builds from one codebase
- Rolling back by republishing the previous bundle, with no store involvement at all
The store rules, and where teams get this wrong
Both major stores permit JavaScript-level updates provided they do not materially change the purpose of the app or introduce features that were never reviewed. In practice the line is about intent: fixing and refining what was approved is fine, using OTA to smuggle in a feature you expected to be rejected is not, and doing so risks the account rather than just the release. The other common mistake is quieter — shipping a bundle that was never tested against the oldest runtime version still in the wild. If you support two or three binary versions simultaneously, your update needs to be safe on all of them, which means keeping a device or simulator on each supported runtime and actually running the update against it.
A delivery setup that holds up in production
One channel per environment, mapped to a branch
Internal, beta and production channels fed from the pipeline mean a merge to main can publish to beta automatically while production stays a deliberate action. Ad-hoc publishing from a developer's laptop is how the wrong bundle reaches real users.
Bump the runtime version whenever native changes
Tie it to your native configuration rather than setting it by hand, so an update can never be delivered to a binary that cannot run it. This single rule prevents the majority of OTA-related crashes.
Roll out in stages and watch crash-free sessions
Publish to a small percentage, compare the crash-free rate and key funnel events against the previous bundle for a few hours, then continue. Without that comparison a staged rollout is just a slower way to ship the same problem.
Decide how aggressively updates apply
Checking on launch and applying on next start is invisible and safe; forcing an immediate reload mid-session is faster but interrupts people. Pick per situation, and never block the splash screen on a network request that might time out.
Keep the store release cadence anyway
OTA is for urgency, not for avoiding native releases. Teams that let the binary drift for months accumulate a native upgrade so large that the next store release becomes a project in itself, and they lose the ability to fix anything below the JavaScript layer.
It is worth being clear about what this does not solve. Over-the-air updates do not reduce the need for testing; they change the cost of being wrong, in both directions. A fix reaches users in an hour, and so does a regression — to users who did not choose to install it and cannot easily go back. The teams that use OTA well are noticeably stricter about their pipeline than the teams that do not, because the safety net of a review queue is gone.
Sequenced sensibly, the pipeline is simple: pull requests run the JavaScript tests and build a preview, merges publish to a beta channel, production publishes are a deliberate staged rollout with monitoring, and native binaries ship on a regular cadence regardless of whether anything urgent forced them. That arrangement gives a small mobile team something close to web deployment speed for the changes that can be made safely, without pretending the native layer has gone away.
Working on something like this?
Object Infotech provides app development for teams that need it from Surat, India. Cross-platform and native mobile apps built with React Native, Flutter, Swift and Kotlin. See projects we have delivered or tell us about yours.
Talk to our team






