BuildBot

React, Off the Web

EAS: build, submit, update

Lesson 5 of 5

What you'll learn

  • Configure EAS Build profiles for development, preview, and production
  • Ship to testers via internal distribution and to stores via eas submit
  • Push over-the-air updates with expo-updates — and know their hard limit

Shipping a web app is git push. Shipping a native app means compiling signed iOS and Android binaries, and EAS Build does that in the cloud — no local Xcode or Android Studio required. Behavior is driven by named profiles in eas.json:

// eas.json
{
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview"
    },
    "production": {
      "autoIncrement": true,
      "channel": "production"
    }
  },
  "submit": { "production": {} }
}

The three profiles map to the three ways a build gets used:

  • development — includes the dev client (a custom Expo Go with your native modules, solving lesson 3's config-plugin gap) and loads JS from your dev server.
  • preview — a release build with distribution: "internal": EAS hosts the artifact at an install URL for registered test devices. No store review, minutes from commit to a tester's phone.
  • production — store-ready, auto-incrementing build numbers; eas submit --platform ios (or android) uploads straight to App Store Connect / Google Play. Then Apple review begins: usually a day or two, sometimes longer, occasionally a rejection.
npx eas build --profile preview --platform all
npx eas submit --profile production --platform ios

OTA updates: the escape hatch, with a fence

Review latency is why expo-updates exists. Your app's JS bundle and assets are downloadable, so eas update publishes a new bundle to a channel (see the profiles above) and installed apps fetch it on next launch — typo fixed in minutes, no review.

The fence has two rails. First, OTA is JS and assets only: adding a native module, changing a config plugin, or bumping the SDK changes native code, which only a new build can deliver. Expo tracks this with runtimeVersion — an update only installs on builds whose runtime version matches, so a mismatched update is never applied. Second, store policy: OTA is for fixes and content within your app's reviewed purpose, not for shipping a materially different app around review.

Know which side of the fence you're on

The expensive failure is publishing an OTA update that silently requires native code that older builds don't have. Correct runtimeVersion hygiene makes this impossible; sloppy hygiene makes it a production crash.

The challenge models the release decision your team makes on every change: OTA, or new build?

OTA or new build? (JS model)

Run it. The classifier applies the real rule: native changes require a build (and a runtimeVersion bump); JS-and-asset changes can ship OTA. Add a change of your own and predict the verdict.

Loading editor…
Knowledge check

You added a new native module via a config plugin. Can eas update deliver this change to existing installs?

That's the module: React you already knew, primitives and navigation you now know, and a pipeline that puts it on real phones — go ship something to a device.

Saved on this device. Sign in to sync your progress everywhere.