Zero-Downtime Deployment: Master Over-the-Air (OTA) Updates in Expo React Native


Have you ever wondered why web developers can push code changes and users see them instantly—yet in mobile apps, even a tiny text fix can take days waiting for Apple or Google’s approval? Every React Native developer has faced that frustration, especially when production bugs sneak in right after release.

That’s where Over-the-Air (OTA) updates come in. In simple terms, OTA updates let you deliver new JavaScript code, images, and other assets directly to users’ devices—without going through the app store review process. It’s like refreshing a web page, but for your mobile app. With Expo’s EAS Updates, this process becomes seamless, secure, and fully integrated into your deployment workflow.

In this guide, you’ll learn how to set up and use EAS Updates for your Expo React Native project, following a clear, step-by-step approach. By the end, you’ll be able to ship updates instantly, fix issues in minutes, and make deployment virtually painless.




Prerequisites

Before diving in, make sure you have the following essentials ready:

  • An Existing Expo Project: Your project must be configured to use EAS (Expo Application Services).
  • EAS CLI Installed: You need the EAS command-line tool.

    npm install -g eas-cli
    
  • An Initial Native Build: A crucial point—OTA updates can only be pushed to an app that was originally built using eas build. You must have a native binary (.ipa or .apk) installed on your test devices or available to your users.

  • Expo Account: Logged in via the CLI (eas login).




Step 1: Configure Your Project for Updates

EAS takes care of much of the heavy lifting, but we need to ensure the configuration is explicit, especially regarding channels. Channels are how you target updates to different environments (e.g., preview, development, production).
To initialize EAS Update configuration run

eas update:configure
Enter fullscreen mode

Exit fullscreen mode

This command will update your app.config.js (or app.json) file with the runtimeVersion and updates.url properties.



1.1 Configure the update channel

The channel property on a build allows you to point updates at specific types of builds. For example, it allows you to publish updates to a preview build without impacting your production deployment.

If you are using EAS Build, eas update:configure will set the update channel property on the preview and production profiles in eas.json. Set them manually if you use different profile names.

// eas.json

{
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "environment": "development",
      "channel": "development"
    },
    "preview": {
      "distribution": "internal",
      "environment": "preview",
      "channel": "preview"
    },
    "production": {
      "environment": "production",
      "channel": "production"
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode



Step 2: Integrate expo-updates (Optional but Recommended)

While Expo handles automatic updates in the background, you might want to add a user interface (UI) to check for and apply updates immediately. This is particularly useful in development or for critical fixes.



2.1 Install the Module

If it’s not already installed, add the official updates module:

npx expo install expo-updates
Enter fullscreen mode

Exit fullscreen mode



2.2 Manually Check for Updates (Optional)

You can create a custom hook or function to check for updates when the app starts. This example shows how to use the useUpdates() hook in a functional component.

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

export default function UpdatesDemo() {
  const {
    currentlyRunning,
    isUpdateAvailable,
    isUpdatePending
  } = Updates.useUpdates();

  useEffect(() => {
    if (isUpdatePending) {
      // Update has successfully downloaded; apply it now
      Updates.reloadAsync();
    }
  }, [isUpdatePending]);

  // If true, we show the button to download and run the update
  const showDownloadButton = isUpdateAvailable;

  // Show whether or not we are running embedded code or an update
  const runTypeMessage = currentlyRunning.isEmbeddedLaunch
    ? 'This app is running from built-in code'
    : 'This app is running an update';

  return (
    <View style={styles.container}>
      <Text style={styles.headerText}>Updates Demo</Text>
      <Text>{runTypeMessage}</Text>
      <Button onPress={() => Updates.checkForUpdateAsync()} title="Check manually for updates" />
      {showDownloadButton ? (
        <Button onPress={() => Updates.fetchUpdateAsync()} title="Download and run update" />
      ) : null}
      <StatusBar style="auto" />
    </View>
  );
}

Enter fullscreen mode

Exit fullscreen mode




Step 3: Build and Distribute the Native Container

Remember, OTA updates only change JavaScript and assets. If you change native code (e.g., add a new library or modify Podfile), you must create a new native build (.apk or .ipa).

Use the eas build command to create the initial app binary that contains the necessary update configuration.

# Build a preview binary for iOS
eas build --profile preview --platform ios
# Build a preview binary for Android
eas build --profile preview --platform android
Enter fullscreen mode

Exit fullscreen mode

Once this build is installed on the device, you are ready for the magic of instant updates.




Step 4: Publishing Your First OTA Update

Now for the moment of truth. You’ve fixed a typo, adjusted a style, or added a new feature that doesn’t involve native module changes.

Instead of running a full, time-consuming native build, you run a single command: eas update.



4.1 The Command

Use the --channel flag to target the specific channel defined in your eas.json (e.g., preview). remember will build for preview that is the reason where pushing to OTA to preview if you’re pushing for production you will use production

# Deploy a new update to all users with the preview binary installed on android
eas update --channel preview --platform android --message "Fixing that embarrassing typo on the homepage. 😅"

# Deploy a new update to all users with the production binary installed on ios
eas update --channel production --platform ios --message "Fixing that embarrassing typo on the homepage. 😅"
Enter fullscreen mode

Exit fullscreen mode



4.2 What Happens Next?

  1. Bundling: EAS bundles your latest JavaScript and static assets.
  2. Upload: The bundle is uploaded to the Expo CDN (Content Delivery Network).
  3. Assignment: The update is assigned to the specified branch (preview or production).
  4. Deployment: When a user opens their existing app, it checks the EAS Update server, downloads the new JavaScript bundle in the background, and applies it upon the next app restart or manually via your expo-updates implementation.



Step 5: Testing the Deployed Update

To confirm your update works:

  1. Make a Visible Change: Change a simple Text component in your app (e.g., change “Welcome” to “Welcome Back!”).
  2. Publish the Update: Run the eas update command from Step 4.
  3. Test:
    • Open your app on the device that has the native build from Step 3.
    • Crucial: Close the app completely (kill it from the task switcher) and re-open it.
    • You should see your new change instantly, without downloading anything from the App Store or installing !



Final Thoughts

Adopting EAS Updates transforms your deployment workflow. No longer are you beholden to slow review processes for minor fixes. You gain speed, agility, and a much better user experience by instantly delivering improvements.

While the freedom is great, remember: any change to native code still requires a full eas build and an App Store submission. For everything else—JS, assets, business logic—eas update is your new best friend! Happy instant deploying! 🎉



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *