Skip to main content

Configuring Over-the-Air Updates in React Native with Expo Updates

· 8 min read
Mobile Developer
Last updated on August 14, 2025

React native ota updates

Ever wished you could update your app’s features or fix bugs without waiting for app store approvals? With over-the-air (OTA) updates, you can! OTA updates let you push new JavaScript code and assets directly to your React Native app, so users get the latest changes instantly.

In this friendly guide, we’ll walk you through setting up OTA updates using Expo Updates with Expo SDK 51, which works beautifully with React Native 0.75.4. Whether you’re new to React Native or just curious about OTA, this tutorial has you covered—no prior experience needed. Want a head start? Check out prebuilt templates with OTA support at Instamobile or Dopebase.

By the end, you’ll have a React Native app ready to roll out updates seamlessly to iOS and Android users, with separate setups for staging and production environments. Let’s dive in!


Prerequisites

Before we get started, make sure you have these tools ready:

  • Node.js (v20.x or later, LTS recommended): Grab it from nodejs.org.
  • npm (v10.x or later, comes with Node.js).
  • Expo CLI: Our go-to for easy React Native setup.
  • Android Studio (2024.3.2 or later): For Android development, with Android SDK 35 and NDK r25+.
  • Xcode (16+): For iOS development (macOS only). You’ll need Xcode 16+ (iOS 18 SDK) to meet App Store requirements.
  • A code editor like VS Code.
  • Git: For version control and managing your code.

Got everything? Awesome—let’s set up your project!


Step 1: Create Your React Native Project

Let’s kick things off by setting up a fresh React Native project with Expo.

  1. Install Expo CLI globally to make project setup a breeze:

    npm install -g expo-cli
  2. Create a new project called MyApp:

    expo init MyApp

    Choose the blank (TypeScript) template for a modern, type-safe setup. Expo SDK 51 powers this project, working smoothly with React Native 0.75.4.

  3. Navigate to your project folder:

    cd MyApp
  4. Add the expo-updates package for OTA magic:

    npm install [email protected]
  5. Test your setup to make sure everything’s working:

    npx expo start

    Press a for Android or i for iOS (macOS only) to launch the app on an emulator or device. You should see a simple welcome screen.

Summary: You’ve set up a new React Native project with Expo SDK 51 and added expo-updates to enable OTA updates.


Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount 🔥

Get the Mega Bundle

Step 2: Set Up Expo Updates

Now that your project is ready, let’s configure Expo Updates to handle OTA updates. This step connects your app to Expo’s cloud service, so you can push updates directly to users.

Project Configuration

  1. Update app.json to enable OTA updates:

    {
    "expo": {
    "updates": {
    "enabled": true,
    "url": "https://u.expo.dev/your-project-id",
    "checkAutomatically": "ON_LOAD",
    "fallbackToCacheTimeout": 0
    },
    "ios": {
    "bundleIdentifier": "com.myapp.app"
    },
    "android": {
    "package": "com.myapp.app"
    }
    }
    }

    You’ll get your project-id in Step 3 when we set up EAS (Expo Application Services).

  2. For bare workflows, link expo-updates:

    • iOS: Run npx pod-install to set up CocoaPods dependencies.
    • Android: Add the following to android/app/build.gradle:
      apply from: "../../node_modules/expo-updates/expo-updates.gradle"
  3. Add update-checking logic to src/App.tsx:

    import { useEffect } from 'react';
    import { View, Text } from 'react-native';
    import * as Updates from 'expo-updates';

    export default function App() {
    useEffect(() => {
    const checkForUpdates = async () => {
    try {
    const update = await Updates.checkForUpdateAsync();
    if (update.isAvailable) {
    await Updates.fetchUpdateAsync();
    await Updates.reloadAsync();
    }
    } catch (e) {
    console.error('Update check failed:', e);
    }
    };
    checkForUpdates();
    }, []);

    return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
    <Text>Welcome to MyApp!</Text>
    </View>
    );
    }

    This code checks for new updates when the app starts, downloads them, and reloads the app to apply them.

iOS Configuration

  1. Open ios/MyApp.xcworkspace in Xcode and confirm ExpoUpdates is linked in the Pods target.
  2. Add the EXUpdatesURL key to Info.plist with the value https://u.expo.dev/your-project-id.
  3. Ensure the app’s bundle identifier matches app.json.

Android Configuration

  1. Update android/app/src/main/res/values/strings.xml:
    <resources>
    <string name="expo_updates_url">https://u.expo.dev/your-project-id</string>
    </resources>
  2. Ensure MainApplication.java includes expo-updates:
    import expo.modules.updates.UpdatesPackage;

    public class MainApplication extends Application implements ReactApplication {
    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected List<ReactPackage> getPackages() {
    return Arrays.asList(
    new MainReactPackage(),
    new UpdatesPackage()
    );
    }
    };
    }

Summary: You’ve configured Expo Updates in your app, setting up the foundation for OTA updates. Next, we’ll connect to EAS to manage those updates.


Step 3: Connect to EAS for OTA Updates

With your app configured, let’s hook it up to Expo Application Services (EAS), the cloud platform that powers OTA updates by hosting your JavaScript bundles.

  1. Log in to Expo:

    eas login

    Follow the browser prompt to authenticate your account.

  2. Set up EAS for your project:

    eas build:configure

    This creates an eas.json file:

    {
    "build": {
    "development": {
    "developmentClient": true,
    "distribution": "internal"
    },
    "preview": {
    "distribution": "internal"
    },
    "production": {}
    }
    }
  3. Initialize OTA updates:

    eas update:configure

    This generates a project ID, which you’ll add to app.json under updates.url.

  4. Build your app to test OTA compatibility:

    eas build --profile preview --platform all

    This creates iOS and Android binaries ready for OTA updates.

Summary: You’ve connected your app to EAS, setting up the cloud infrastructure to deliver OTA updates. Now, let’s try pushing your first update!


Step 4: Deploy Your First OTA Update

Now that your app is linked to EAS, let’s deploy an update to see OTA in action.

  1. Make a small tweak to your app, like updating the welcome message in src/App.tsx:

    <Text>Welcome to MyApp! (Updated)</Text>
  2. Publish the update to the production branch:

    eas update --branch production

    This uploads your new JavaScript bundle to EAS.

  3. Test the update:

    • Run your app in preview or production mode (avoid Expo Go, as it may interfere with updates).
    • Close and reopen the app to download the update.
    • Check that the updated message appears.
  4. For a staging environment, publish to a separate branch:

    eas update --branch staging

    Use the EAS dashboard to manage branches or roll back updates if needed.

Summary: You’ve successfully deployed an OTA update, and your app now reflects the latest changes without an app store submission.


Step 5: Set Up Staging and Production Environments

To keep things organized, let’s configure separate OTA updates for staging and production environments, so you can test changes safely before going live.

  1. Create a dynamic app.config.ts for environment-specific settings:

    import { ConfigContext, ExpoConfig } from 'expo/config';

    export default ({ config }: ConfigContext): ExpoConfig => ({
    ...config,
    name: process.env.APP_ENV === 'staging' ? 'MyApp Staging' : 'MyApp',
    slug: 'myapp',
    updates: {
    enabled: true,
    url: `https://u.expo.dev/your-project-id`,
    checkAutomatically: 'ON_LOAD',
    fallbackToCacheTimeout: 0,
    },
    ios: {
    bundleIdentifier: process.env.APP_ENV === 'staging' ? 'com.myapp.staging' : 'com.myapp.app',
    },
    android: {
    package: process.env.APP_ENV === 'staging' ? 'com.myapp.staging' : 'com.myapp.app',
    },
    });
  2. Build for each environment:

    • Staging: eas build --profile preview --platform all
    • Production: eas build --profile production --platform all
  3. Publish updates for each environment:

    APP_ENV=staging eas update --branch staging
    APP_ENV=production eas update --branch production
  4. Test the environments:

    • Install the staging build and verify the staging branch updates.
    • Install the production build and confirm the production branch updates.

Summary: You’ve set up separate staging and production environments, allowing safe testing of updates before they reach all users.


Step 6: Enhance Your OTA Workflow

With your OTA system up and running, let’s explore ways to make it even better.

  • Rollbacks: If an update goes wrong, revert it using the EAS dashboard or CLI:
    eas update:rollback --branch production
  • A/B Testing: Create branches like experiment-a and experiment-b to test new features with different user groups.
  • Analytics: Add expo-analytics to track how users interact with updates and catch errors early.
  • Optimize Updates: Keep update sizes small by excluding unused assets and using code-splitting techniques.

Summary: Enhance your OTA setup with rollbacks, A/B testing, analytics, and optimization for a smooth and reliable update process.


Troubleshooting Tips

  • Updates Not Loading: Ensure you’re not in debug mode, as it skips OTA updates. Test with preview or production builds.
  • iOS Issues: Verify the EXUpdatesURL in Info.plist matches your EAS project ID.
  • Android Problems: Check expo-updates linking in android/app/build.gradle and confirm expo_updates_url is set in strings.xml.
  • EAS Setup Errors: If eas update:configure fails, manually check eas.json and your project ID.

Looking for a custom mobile application?

Our team of expert mobile developers can help you build a custom mobile app that meets your specific needs.

Get in Touch

Conclusion

You’ve built a React Native app with OTA updates using Expo Updates and Expo SDK 51, making it easy to push new features and fixes to iOS and Android users instantly. With separate staging and production environments, you’re ready to test and deploy like a pro. Want to speed things up? Explore prebuilt templates with OTA support at Instamobile or Dopebase. For more advanced features, check out Expo’s documentation or join the React Native community at reactnative.dev.

Additional Resources