EAS Build
Expo Application Services (EAS) builds, submits, and updates React Native apps without you babysitting Xcode or Android Studio. eas build runs the native build in the cloud, eas submit ships to the stores, and eas update pushes JS+assets OTA. The free tier covers small teams; paid tiers buy concurrency.
eas.json, build, OTA update, and submit
EXAMPLE
# 1) Install + login
npm i -g eas-cli
eas login
# 2) Configure the project once
eas init # creates eas.json + links to a project on expo.dev
# 3) eas.json — three sensible profiles
{
"cli": { "version": ">= 7.0.0" },
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"channel": "development",
"env": { "API_BASE": "https://staging.api.example.com" }
},
"preview": {
"distribution": "internal",
"channel": "preview",
"ios": { "simulator": true },
"env": { "API_BASE": "https://staging.api.example.com" }
},
"production": {
"channel": "production",
"autoIncrement": true,
"env": { "API_BASE": "https://api.example.com" }
}
},
"submit": {
"production": {
"ios": { "ascAppId": "6471234567" },
"android": { "track": "internal" }
}
}
}
# 4) Run a build for each platform
eas build --platform ios --profile preview
eas build --platform android --profile preview
eas build --platform all --profile production
# 5) Local builds — same eas.json, runs on your machine (saves cloud quota)
eas build --platform ios --profile preview --local
# 6) Over-the-air JS updates — ship a JS change WITHOUT a store review
# Step a) configure expo-updates in app.config.ts:
# {
# updates: { url: 'https://u.expo.dev/<project-id>' },
# runtimeVersion: { policy: 'appVersion' },
# }
# Step b) build + ship to the store ONCE, then OTA forever (within same runtime)
eas update --branch production --message 'Fix checkout copy'
# Force users onto the latest update on next launch (in app code):
# import * as Updates from 'expo-updates';
# const up = await Updates.checkForUpdateAsync();
# if (up.isAvailable) { await Updates.fetchUpdateAsync(); await Updates.reloadAsync(); }
# 7) Submit to the stores after a successful build
eas submit --platform ios --latest --profile production
eas submit --platform android --latest --profile production
# 8) Credentials management — eas keeps signing keys in a managed vault
eas credentials # interactive viewer
eas credentials:configure-build --platform ios --profile production
# 9) Common gotchas
# - Native module added? Build a new binary; OTA cannot ship native code.
# - runtimeVersion mismatch? Devices will refuse the OTA. Bump it deliberately.
# - .env values: only those declared in eas.json 'env' or app.config get bundled.
# Use EAS Secrets for sensitive values: eas secret:create SENTRY_DSN ...
# 10) CI integration — eas build runs unattended in GitHub Actions
# .github/workflows/build.yml (sketch)
# - run: npm ci
# - run: npx eas-cli build --platform all --profile preview --non-interactive
# env: { EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }} }
Why it matters
OTA updates are the unlock — once the binary is signed and approved, every JS-only change ships in seconds without a store review. Just keep runtimeVersion honest: bump it the moment you add a native module, otherwise old devices will fetch an OTA that references native code they do not have and crash on boot.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
npx expo install expo-dev-client npm install -g eas-cli eas build -p ios --profile preview eas build -p android --profile productionTry it Yourself »
Exercise
Run an iOS production build via EAS.
eas build -p ios --profile
Ten letters.
Discussion
Loading…