Over-the-Air Updates

React Native OTA updates without the wait

Ship JS-layer fixes and UI changes to your React Native app in seconds, not days. Ubriot publishes OTA updates to named channels and lets you roll back instantly.

What Ubriot OTA updates give your React Native team

  • Publish updates without App Store review
  • Named channels (production, staging, etc.)
  • Runtime version compatibility checks
  • Instant rollback from the dashboard
  • CLI command: ubriot update
  • Update history and bundle storage
  • Per-channel update targeting
  • Works with expo-updates compatible clients

How React Native OTA updates work with Ubriot

Set up once, then ship updates with one command.

Step 1

Set a runtime version in app.json

The runtime version tells Ubriot which native binary a JS bundle is compatible with. Updates only land on matching binaries.

Step 2

Set up the update URL

Point expo-updates (or your update client) at your Ubriot update endpoint. The app checks for a new bundle on launch.

Step 3

Run ubriot update

Bundle your JS layer and publish it to a channel - production, staging, or any name you choose. Takes seconds.

Step 4

The app receives the update

On next launch, the app downloads and applies the new bundle. No App Store review, no waiting.

Step 5

Roll back instantly if needed

If a bad update ships, roll back to any previous bundle from the dashboard. Users get the previous version on next launch.

Common pitfalls

These mistakes cause silent OTA failures - good to know before you ship.

reloadAsync() must be compiled into the binary - it cannot arrive via OTA

When checkAutomatically is set to NEVER, your app code must call checkForUpdateAsync() → fetchUpdateAsync() → reloadAsync() manually. If a binary ships without reloadAsync(), the app downloads updates silently but never applies them. Any OTA that adds reloadAsync() will also never apply, creating a permanent loop. The only escape is a new binary build. Always verify the full three-call sequence is in your binary before shipping.

Run ubriot update from inside your app directory, not the monorepo root

In a yarn workspaces monorepo, yarn hoists packages to the root. When you run ubriot update from the root, npx expo export resolves modules from the wrong node_modules and fails with module-not-found errors. Always cd into your app folder (e.g. products/my-app/mobile) before running ubriot update.

runtimeVersion links OTAs to binaries - never change it casually

An OTA is only delivered to devices whose installed binary declares the same runtimeVersion. Changing the value in app.json means old binaries stop receiving new OTAs instantly, and new OTAs can never reach anyone still on the old binary. Only change runtimeVersion when a native module change makes old JS bundles genuinely incompatible with the new binary.

Apple closes a version train once a build is approved

If you submit a binary and Apple approves it, that version string (CFBundleShortVersionString) is closed. Submitting another binary under the same version returns a 409 Invalid Pre-Release Train error. Bump your version field in app.json before every binary build that goes to TestFlight or the App Store.

Ship React Native OTA updates today

Fix bugs and ship UI changes in seconds with ubriot update.

Get started