Expo EAS Build and Deployment: From Development to App Store
Expo Application Services (EAS) has become the gold standard for React Native build and deployment. EAS Build handles native compilation in the cloud, EAS Submit automates store submissions, and EAS Update enables instant over-the-air updates.
Setting Up EAS
Installation and Initialization
npm install -g eas-cli
eas login
eas init --id your-project-id
eas.json Configuration
{
"cli": {
"version": ">= 7.0.0",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"env": {
"APP_ENV": "development",
"API_URL": "https://api-dev.example.com"
}
},
"staging": {
"distribution": "internal",
"env": {
"APP_ENV": "staging",
"API_URL": "https://api-staging.example.com"
},
"android": {
"buildType": "apk"
},
"ios": {
"simulator": false
}
},
"production": {
"autoIncrement": true,
"env": {
"APP_ENV": "production",
"API_URL": "https://api.example.com"
},
"android": {
"buildType": "app-bundle"
}
}
},
"submit": {
"production": {
"android": {
"serviceAccountKeyPath": "./service-account.json",
"track": "internal"
},
"ios": {
"appleId": "your@apple.com",
"ascAppId": "1234567890",
"appleTeamId": "TEAM123"
}
}
}
}
Multi-Environment Configuration
app.config.js with Environment Variables
// app.config.js
const IS_DEV = process.env.APP_ENV === 'development';
const IS_STAGING = process.env.APP_ENV === 'staging';
export default {
name: IS_DEV ? 'MyApp (Dev)' : IS_STAGING ? 'MyApp (Staging)' : 'MyApp',
slug: 'my-app',
version: '1.0.0',
icon: IS_DEV ? './assets/icon-dev.png' : './assets/icon.png',
android: {
package: IS_DEV
? 'com.example.myapp.dev'
: IS_STAGING
? 'com.example.myapp.staging'
: 'com.example.myapp',
},
ios: {
bundleIdentifier: IS_DEV
? 'com.example.myapp.dev'
: IS_STAGING
? 'com.example.myapp.staging'
: 'com.example.myapp',
},
extra: {
apiUrl: process.env.API_URL,
appEnv: process.env.APP_ENV,
eas: { projectId: 'your-project-id' },
},
updates: {
url: 'https://u.expo.dev/your-project-id',
},
runtimeVersion: {
policy: 'appVersion',
},
};
EAS Build Workflow
Building for All Platforms
# Development build
eas build --platform all --profile development
# Staging build
eas build --platform android --profile staging
# Production build
eas build --platform all --profile production --non-interactive
Custom Build Hooks
// eas.json additions
{
"build": {
"production": {
"buildArtifactPaths": ["ios/build/MyApp.ipa"],
"prebuildCommand": "node scripts/pre-build.js",
"cache": {
"key": "my-cache-key-v1",
"paths": [
"node_modules",
"ios/Pods"
],
"disabled": false
}
}
}
}
EAS Update: OTA Updates
Configuration
npx expo install expo-updates
eas update:configure
In your app:
import * as Updates from 'expo-updates';
async function checkForUpdates() {
if (__DEV__) return;
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}
} catch (error) {
console.error('Update check failed:', error);
}
}
// In App.tsx
useEffect(() => {
checkForUpdates();
}, []);
Update Channels and Rollback
# Publish update to staging channel
eas update --channel staging --message "Fix login bug"
# Publish to production
eas update --channel production --message "v1.2.1 hotfix"
# List updates
eas update:list --channel production
# Rollback (republish previous update)
eas update:republish --channel production --group <update-group-id>
Runtime Version Strategy
// eas.json
{
"build": {
"production": {
"channel": "production",
"runtimeVersion": "1.0.0"
}
}
}
App Signing Management
Android Keystore
# EAS manages credentials automatically
eas credentials --platform android
# Or provide your own
eas credentials --platform android --profile production
iOS Certificates and Profiles
# Auto-manage iOS credentials
eas build --platform ios --profile production
# EAS will create/renew certificates automatically
# View credentials
eas credentials --platform ios
CI/CD Integration
GitHub Actions Workflow
# .github/workflows/eas-build.yml
name: EAS Build
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
name: Build and Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Setup EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: Build on EAS
run: eas build --platform all --profile production --non-interactive
- name: Submit to stores
if: github.ref == 'refs/heads/main'
run: eas submit --platform all --profile production --non-interactive
App Store Submission
# Submit after successful build
eas submit --platform ios --latest --profile production
# Submit specific build
eas submit --platform android --id <build-id> --profile production
# Auto-submit after build
eas build --platform all --profile production --auto-submit
Monitoring and Analytics
import * as Updates from 'expo-updates';
import * as Sentry from '@sentry/react-native';
// Track update adoption
Sentry.setTag('update_id', Updates.updateId ?? 'embedded');
Sentry.setTag('runtime_version', Updates.runtimeVersion);
Sentry.setTag('channel', Updates.channel);
Sentry.setTag('app_env', process.env.APP_ENV);
Conclusion
EAS Build and Deploy provides a complete, production-grade CI/CD pipeline for React Native apps. The combination of cloud builds, OTA updates, automated signing, and store submission eliminates most of the operational complexity of mobile deployment. Teams can focus on building features while EAS handles the infrastructure, making it the most efficient path from code to users' devices.