Skip to main content

Deployment & Distribution

Learn how to prepare, build, and distribute your React Native app to the App Store and Google Play Store using Expo Application Services (EAS).

Overview

Deployment and distribution is the final step in bringing your React Native app to users. This guide covers the entire process from preparing your app for production to publishing on app stores.

Table of Contents

  1. Pre-deployment Checklist
  2. EAS Build Configuration
  3. App Store Preparation
  4. Building for Production
  5. App Store Submission
  6. Google Play Store
  7. Over-the-Air Updates
  8. Monitoring & Analytics
  9. Best Practices

Pre-deployment Checklist

Code Quality

// Ensure all TypeScript errors are resolved
// Run comprehensive tests
npm run test
npm run type-check
npm run lint

// Check for unused dependencies
npx depcheck

// Verify app performance
npm run performance-test

App Configuration

// app.json - Production configuration
{
"expo": {
"name": "Your App Name",
"slug": "your-app-slug",
"version": "1.0.0",
"orientation": "portrait",
"privacy": "public",
"platforms": ["ios", "android"],
"icon": "./assets/images/icon.png",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.yourcompany.yourapp",
"buildNumber": "1"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#FFFFFF"
},
"package": "com.yourcompany.yourapp",
"versionCode": 1
}
}
}

Assets & Resources

// Optimize images and assets
const assetOptimization = {
images: {
// Use WebP format when possible
format: "webp",
// Provide multiple resolutions
resolutions: ["@1x", "@2x", "@3x"],
// Compress appropriately
quality: 80,
},

fonts: {
// Include only used font weights
weights: ["400", "600", "700"],
// Use system fonts when possible
fallbacks: ["system"],
},
}

EAS Build Configuration

Installing EAS CLI

# Install EAS CLI globally
npm install -g @expo/eas-cli

# Login to your Expo account
eas login

# Initialize EAS in your project
eas build:configure

EAS Configuration

// eas.json
{
"cli": {
"version": ">= 5.4.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal",
"ios": {
"simulator": true
}
},
"production": {
"autoIncrement": true,
"env": {
"NODE_ENV": "production"
}
}
},
"submit": {
"production": {
"ios": {
"appleId": "your-apple-id@example.com",
"ascAppId": "1234567890",
"appleTeamId": "XXXXXXXXXX"
},
"android": {
"serviceAccountKeyPath": "./google-service-account.json",
"track": "production"
}
}
}
}

Environment Variables

// Managing environment variables
// .env.production
API_URL=https://api.yourapp.com
ANALYTICS_KEY=prod_analytics_key
SENTRY_DSN=https://sentry.dsn

// Using environment variables
import Constants from 'expo-constants';

const config = {
apiUrl: Constants.expoConfig?.extra?.apiUrl,
analyticsKey: Constants.expoConfig?.extra?.analyticsKey,
};

App Store Preparation

iOS App Store Connect

// iOS specific configurations
const iosConfig = {
bundleIdentifier: "com.yourcompany.yourapp",
teamId: "XXXXXXXXXX",

// App Store Connect information
appStoreConnect: {
appId: "1234567890",
categories: ["Productivity", "Business"],
contentRating: "4+",
description: "Your app description",
keywords: "productivity, business, mobile",
supportUrl: "https://yourapp.com/support",
privacyUrl: "https://yourapp.com/privacy",
},
}

Screenshots & Metadata

// Screenshot requirements
const screenshotRequirements = {
iPhoneSE: "1242 x 2208",
iPhone8Plus: "1242 x 2208",
iPhone11Pro: "1125 x 2436",
iPhone12ProMax: "1284 x 2778",
iPadPro: "2048 x 2732",

// Android
phone: "1080 x 1920",
sevenInchTablet: "1200 x 1920",
tenInchTablet: "1600 x 2560",
}

Building for Production

iOS Build

# Build for iOS App Store
eas build --platform ios --profile production

# Build for TestFlight
eas build --platform ios --profile preview

# Check build status
eas build:list

Android Build

# Build for Google Play Store
eas build --platform android --profile production

# Build APK for testing
eas build --platform android --profile preview --format apk

# Build AAB (recommended for Play Store)
eas build --platform android --profile production --format aab

Build Optimization

// Metro bundler optimization
// metro.config.js
module.exports = {
transformer: {
minifierConfig: {
mangle: {
keep_fnames: true,
},
},
},
resolver: {
alias: {
"@": "./src",
},
},
}

// Hermes engine optimization (Android)
const androidConfig = {
enableHermes: true,
proguardMinifyEnabled: true,
shrinkResources: true,
}

App Store Submission

iOS Submission

# Submit to App Store Connect
eas submit --platform ios --profile production

# Or submit manually with Transporter app
# Download .ipa from EAS and upload via Transporter

App Store Review Guidelines

// Common rejection reasons to avoid
const avoidRejections = {
privacy: {
// Include privacy policy
privacyPolicyUrl: "https://yourapp.com/privacy",
// Request permissions appropriately
permissionDescriptions: {
camera: "Used to scan QR codes",
location: "Used to find nearby stores",
},
},

functionality: {
// Ensure app works offline if applicable
offlineSupport: true,
// No placeholder content
realContent: true,
// Proper error handling
errorHandling: true,
},
}

Google Play Store

Play Console Setup

// Android signing configuration
const androidSigning = {
// EAS handles signing automatically
// Or configure manual signing
signingConfig: {
keyAlias: "your-key-alias",
keyPassword: "your-key-password",
storeFile: "./keystore.jks",
storePassword: "your-store-password",
},
}

Play Store Submission

# Submit to Google Play
eas submit --platform android --profile production

# Upload AAB manually via Play Console
# Or use Google Play CLI

Play Store Optimization

// App Bundle optimization
const bundleConfig = {
// Enable App Bundle
bundleFormat: "aab",

// Dynamic delivery
dynamicFeatures: {
camera: {
onDemand: true,
deliveryType: "on_demand",
},
},

// Asset packs
assetPacks: {
images: {
deliveryType: "install_time",
},
},
}

Over-the-Air Updates

EAS Update Setup

# Install EAS Update
expo install expo-updates

# Configure updates
eas update:configure

# Publish an update
eas update --branch production --message "Bug fixes and improvements"

Update Configuration

// app.json - Update configuration
{
"expo": {
"updates": {
"url": "https://u.expo.dev/your-project-id",
"requestHeaders": {
"expo-channel-name": "production"
}
},
"runtimeVersion": {
"policy": "sdkVersion"
}
}
}

// Check for updates in app
import * as Updates from 'expo-updates';

const checkForUpdates = async () => {
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}
} catch (error) {
console.log('Error checking for updates:', error);
}
};

Monitoring & Analytics

Crash Reporting

// Sentry integration
import * as Sentry from "@sentry/react-native"

Sentry.init({
dsn: "your-sentry-dsn",
environment: __DEV__ ? "development" : "production",
})

// Crashlytics (Firebase)
import crashlytics from "@react-native-firebase/crashlytics"

crashlytics().recordError(new Error("Something went wrong"))

Analytics Setup

// Firebase Analytics
import analytics from "@react-native-firebase/analytics"

const trackEvent = async (eventName: string, parameters: object) => {
await analytics().logEvent(eventName, parameters)
}

// Custom analytics
const trackUserAction = (action: string, screen: string) => {
analytics().logEvent("user_action", {
action,
screen,
timestamp: Date.now(),
})
}

Performance Monitoring

// React Native Performance
import Perf from "react-native-performance"

const performanceMonitoring = {
// Track screen transitions
trackNavigation: (screen: string) => {
Perf.mark(`navigation_to_${screen}`)
},

// Monitor API calls
trackApiCall: (endpoint: string, duration: number) => {
analytics().logEvent("api_call", {
endpoint,
duration,
success: duration < 5000,
})
},
}

Best Practices

Versioning Strategy

// Semantic versioning
const versionStrategy = {
major: "Breaking changes",
minor: "New features",
patch: "Bug fixes",

// Example: 1.2.3
// 1 = Major version
// 2 = Minor version
// 3 = Patch version
}

// Automated versioning
const incrementVersion = (type: "major" | "minor" | "patch") => {
// Use tools like semantic-release or standard-version
// Or EAS autoIncrement feature
}

Release Management

// Release checklist
const releaseChecklist = {
preRelease: [
"Run full test suite",
"Update version numbers",
"Update changelog",
"Test on physical devices",
"Performance testing",
"Security review",
],

release: [
"Build production versions",
"Submit to app stores",
"Update marketing materials",
"Prepare release notes",
"Monitor for issues",
],

postRelease: ["Monitor crash reports", "Track user feedback", "Plan hotfixes if needed", "Analyze adoption metrics"],
}

Rollback Strategy

// Emergency rollback plan
const rollbackStrategy = {
// Over-the-air updates
hotfix: async () => {
await eas.update.publish({
branch: "production",
message: "Emergency rollback",
})
},

// App store rollback
appStore: {
ios: "Use App Store Connect to rollback",
android: "Release previous version with higher version code",
},

// Feature flags for gradual rollout
featureFlags: {
newFeature: {
enabled: false,
percentage: 0,
},
},
}

Common Issues & Solutions

Build Failures

// Common build issues and solutions
const troubleshooting = {
"Metro bundler timeout": {
solution: "Increase metro timeout in metro.config.js",
config: { resetCache: true },
},

"iOS provisioning profile": {
solution: "Update certificates in Apple Developer Portal",
command: "eas credentials",
},

"Android signing": {
solution: "Verify keystore configuration",
check: "eas credentials:configure",
},
}

Store Rejection Solutions

// Common rejection fixes
const rejectionFixes = {
"Missing privacy policy": {
fix: "Add privacy policy URL to app configuration",
location: "app.json or App Store Connect",
},

"Incomplete information": {
fix: "Provide detailed app description and keywords",
requirement: "Minimum 10 words description",
},

"Inappropriate content": {
fix: "Review content guidelines and age rating",
reference: "App Store Review Guidelines",
},
}

Next Steps

Once your app is successfully deployed:

Resources