ExpoEAS UpdateOTA UpdatesDeployment

EAS Update Guide: Over-the-Air (OTA) Updates for Expo Apps

Ship bug fixes to Expo apps in minutes without app store review. Set up EAS Update and expo-updates, configure channels and runtime versions, publish with eas update, run percentage rollouts, roll back, and check for updates in-app with useUpdates.

DeveloperMill4 min read

You ship a release, and an hour later a crash report comes in: a typo in an API URL breaks checkout. Without OTA updates, you'd wait a day or more for app review. With EAS Update, the fix reaches users on their next app launch, usually within minutes.

How OTA updates work

An Expo app has two layers:

  • Native code (the binary): Expo SDK modules, native libraries, permissions, and the icon. This changes only with a new store build.
  • The JavaScript bundle and assets: your screens, logic, styles, and images.

EAS Update replaces the second layer. Each build has a runtime version that identifies its native code, and an update is only delivered to builds with a matching runtime version, so you never ship JavaScript that calls native code the build doesn't have.

1. Set up

npm install -g eas-cli
npx expo install expo-updates
eas update:configure

eas update:configure adds runtimeVersion and updates.url to your app config, and sets a channel on your build profiles in eas.json:

{
  "build": {
    "preview": { "channel": "preview", "distribution": "internal" },
    "production": { "channel": "production" }
  }
}

A channel is baked into each build. Production builds listen to production, and internal test builds listen to preview.

2. Choose a runtime version policy

The runtime version decides which builds can receive which updates:

{
  "expo": {
    "runtimeVersion": { "policy": "fingerprint" }
  }
}
  • fingerprint is computed from your native dependencies and config. It changes automatically when native code changes, which makes it the safest default. (In SDK 58 the default fingerprint preset is balanced, so app version bumps no longer change it.)
  • appVersion uses your version field. It's simple, but you must remember to bump it when native code changes.

3. Build, then publish updates

Create builds once:

eas build --profile production --platform all

After that, ship JavaScript changes with:

eas update --channel production --environment production --message "Fix checkout API URL"

The --environment flag is required on SDK 55+. It selects which EAS environment variables are baked into the update.

4. Roll out gradually

Never send an update to 100% of users at once. Start small:

eas update --channel production --environment production --message "New onboarding copy" --rollout-percentage=10

Watch crash and error rates (for example in Expo Observe), then increase the percentage:

eas update:edit

If something's wrong, revert:

eas update:revert-update-rollout

For larger changes, eas channel:rollout performs a branch-based rollout between two branches of updates.

5. Check for updates in the app

By default, apps check for updates on launch and apply them on the next launch. You can also prompt users with the useUpdates hook:

import * as Updates from "expo-updates"
import { Button, Text, View } from "react-native"

export function UpdateBanner() {
  const { isUpdateAvailable, isUpdatePending } = Updates.useUpdates()

  if (isUpdatePending) {
    return <Button title="Restart to update" onPress={() => Updates.reloadAsync()} />
  }
  if (isUpdateAvailable) {
    return <Button title="Download update" onPress={() => Updates.fetchUpdateAsync()} />
  }
  return null
}

Call Updates.checkForUpdateAsync() when the app returns to the foreground if you want users to get fixes faster.

What you can and can't ship over the air

Can ship OTA:

  • Bug fixes in JavaScript or TypeScript
  • UI and copy changes, styling, and images
  • New screens built only from existing native modules
  • Feature flags and config changes

Needs a new store build:

  • Adding or upgrading a native module (for example MMKV or expo-widgets)
  • Changing permissions, icons, splash screens, or config plugins
  • Upgrading the Expo SDK (SDK 58 upgrade guide)

Best practices

  • Use the fingerprint policy so incompatible updates can't be delivered.
  • Test on preview first with an internal build, then publish to production.
  • Always do staged rollouts and watch error rates.
  • Write useful messages. They're your changelog when debugging.
  • Respect store rules. Use OTA for fixes and iterations, not for shipping a different app.
  • Automate it with EAS Workflows or CI on merge to main.

Next steps

OTA updates handle the after-launch part. For the first release, follow our guide to publishing an Expo app to the App Store and Google Play. Starting a new app? Our Expo templates are ready for EAS Build and EAS Update from day one.

Frequently asked questions

What is EAS Update?

EAS Update is Expo's service for over-the-air (OTA) updates. It lets you publish new JavaScript, styles, and assets to apps that are already installed, so users get bug fixes and small features on the next launch — without submitting a new build to the App Store or Google Play.

Are OTA updates allowed by Apple and Google?

Yes, within limits. Both stores allow updating interpreted code like JavaScript as long as you don't change the app's primary purpose or bypass review with major new features. Use OTA updates for fixes and incremental improvements, and ship big changes through a normal store release.

What can't I change with an OTA update?

Anything native: adding or upgrading a native module, changing app.json native config (permissions, icons, splash screen, plugins), or upgrading the Expo SDK. Those require a new build. The runtime version ensures an update is only delivered to builds with compatible native code.

How do I roll back a bad EAS Update?

If you used a percentage rollout, run `eas update:revert-update-rollout`. Otherwise, republish a previous known-good update to the same channel, or publish a rollback so clients return to the code embedded in their build. Always run new updates as staged rollouts so problems reach only a small share of users.

Build it faster with a template

Production-ready Expo React Native templates with the patterns from this post already wired up.

Browse templates