Updating Apps Without Re-submitting to the Store: How to Use CodePush in React Native?

Updating Apps Without Re-submitting to the Store: How to Use CodePush in React Native | 2026 Guide

Updating Apps Without Re-submitting to the Store: How to Use CodePush in React Native

The OTA Update Revolution in 2026

App Store review times of 24–72 hours for iOS and 3–7 days for Google Play are unacceptable when a critical bug is affecting your users. In 2026, Over-The-Air (OTA) updates have become the standard for React Native teams who need to ship fixes, UI tweaks, and feature flags without waiting for store approval.

OTA updates work because React Native apps are essentially JavaScript bundles running on a native shell. If you can replace the JavaScript bundle without touching the native code, you can update your app instantly — no store review, no user action required.

But the OTA landscape changed dramatically in 2025. Microsoft retired Visual Studio App Center on March 31, 2025, shutting down the hosted CodePush service that thousands of teams relied on. The lesson was clear: OTA updates are too critical to depend on a platform where they're not the core product. citeweb_search:10#0

This guide covers what happened to CodePush, the best alternatives in 2026, and how to implement a production-grade OTA update strategy that ships fixes in minutes, not days.

24-72h iOS App Store Review Time
3-7 Days Google Play Review Time
<2 Min OTA Update Delivery Time
90% Bandwidth Savings with Diff Updates
AdSense Display Ad — 728x90 / Responsive

What Happened to CodePush? The App Center Shutdown

React Native CodePush was the original OTA update tool for the framework. Built by Microsoft and hosted on App Center, it let teams push JavaScript updates straight to user devices. For years, it was the default choice for any bare React Native project.

That changed when Microsoft retired Visual Studio App Center on March 31, 2025. The entire platform shut down — dashboard, CLI login, update delivery, everything. Developers could no longer push updates or manage deployments through App Center. Only Analytics and Diagnostics got an extension to June 2026. citeweb_search:10#0

The shutdown hit thousands of teams who relied on CodePush for production releases. Before the shutdown, Microsoft released a standalone CodePush server as open-source software. This server is fully compatible with the existing react-native-code-push client SDK. You deploy it on your own cloud — AWS, Azure, GCP, or any provider — and manage updates yourself.

The trade-off is clear: you keep the familiar CodePush workflow, but you own the hosting, scaling, and maintenance. Microsoft won't accept issues or code changes on the repo, so bug fixes are your responsibility. For teams with strong DevOps skills, self-hosting works well. For everyone else, a managed alternative is the better path forward. citeweb_search:10#0

The 2026 OTA Landscape: Top Alternatives Compared

With CodePush's hosted service gone, three paths dominate the React Native OTA ecosystem in 2026:

Capability Self-Hosted CodePush EAS Updates Revopush
Migration from CodePush Familiar model, fully self-hosted Different runtime and release model CodePush-compatible SDK
Managed Cloud No Yes Yes
React Native 0.76+ / New Architecture No actively maintained path Yes (expo-updates) Yes
Diff/Patch Updates No publicly documented Hermes bytecode diffing Up to 90% egress reduction
Analytics & Team Collaboration You build and maintain Expo ecosystem Built-in admin panel
Pricing Model Infra + storage + CDN + DevOps Subscription + users + bandwidth Subscription + $0.03/GB over limit
2026 Recommendation DevOps-heavy teams only Default for most teams Fastest CodePush migration

Key Insight: EAS Updates is the strongest choice for most teams in 2026. It's actively maintained by Expo, supports both Expo and bare React Native projects, and includes phased rollouts, auto-rollback, and Hermes bytecode diffing as of SDK 55. For teams that want CodePush SDK compatibility without self-hosting, Revopush is the easiest migration path. citeweb_search:10#0

EAS Updates: The Modern Standard

Recommended for Most Teams

EAS Updates (Expo Application Services Updates) is the most robust OTA solution for React Native in 2026. Built and maintained by Expo, it supports both Expo managed workflow and bare React Native projects. It includes features that CodePush never had: Hermes bytecode diffing, fingerprint-based native change detection, and seamless integration with Expo's build and submit pipeline.

Why EAS Updates Leads in 2026

  • Hermes Bytecode Diffing: Delivers only the changed bytecode between updates, reducing bundle size by up to 90% and cutting bandwidth costs dramatically citeweb_search:10#7
  • Fingerprint Detection: Automatically detects when native code changes and prevents incompatible OTA updates from reaching users citeweb_search:10#2
  • Phased Rollouts: Release to 5% of users first, monitor metrics, then ramp to 100% — all from the CLI citeweb_search:10#2
  • Auto-Rollback: If an update causes crashes, EAS Updates can automatically revert to the previous stable version
  • Channel-Based Deployment: Separate preview, staging, and production channels with different update policies
  • Expo Ecosystem Integration: Works seamlessly with EAS Build, EAS Submit, and Expo Router

🟢 EAS Updates Excels When:

  • You're starting a new React Native project (Expo or bare)
  • You want the most actively maintained OTA platform
  • Hermes bytecode diffing and bandwidth optimization matter
  • You need fingerprint-based native change detection
  • You want integrated build, update, and submit pipelines
  • Your team values phased rollouts and automatic rollback

Revopush: The CodePush Successor

Revopush is the managed alternative that maintains direct compatibility with the CodePush SDK. If your team has existing CodePush integrations and wants the fastest migration path without rewriting your update logic, Revopush is purpose-built for this scenario.

Revopush Key Features

  • CodePush SDK Compatible: Drop-in replacement for react-native-code-push — minimal code changes required
  • React Native 0.76+ Support: Full compatibility with the New Architecture and Bridgeless mode citeweb_search:10#7
  • Diff Updates: Up to 90% bandwidth reduction with patch-based delivery citeweb_search:10#7
  • Built-in Analytics: Admin panel with deployment metrics, adoption rates, and error tracking
  • Managed Infrastructure: No self-hosting required — focus on your app, not your update server

🔴 Revopush Excels When:

  • You have an existing CodePush integration to migrate
  • You want SDK compatibility without self-hosting
  • Your team is on React Native 0.76+ with New Architecture
  • You need diff updates for bandwidth optimization
  • You want a managed service with built-in analytics
AdSense In-Article Ad — 336x280 / Responsive

Self-Hosted CodePush Server

Microsoft published the standalone CodePush Server source code before the App Center shutdown. This path keeps the original deployment model alive but moves the entire operational burden to your team.

Self-Hosting Trade-offs

Factor Pros Cons
Control Full control over infrastructure, data, and customization You own uptime, security patches, and scaling
Cost No per-update fees Hidden costs: servers, CDN, storage, DevOps time
Maintenance Can customize for internal needs Repository archived by Microsoft — no official support
Diff Updates Possible with custom implementation No publicly documented diff-update delivery

🔵 Self-Hosted Excels When:

  • You have a dedicated DevOps team with cloud infrastructure expertise
  • Data sovereignty requirements mandate self-hosted solutions
  • You need deep customization of the update server
  • Your update volume is high enough that managed pricing doesn't make sense

Implementation: EAS Updates Step-by-Step

Here's how to implement EAS Updates in a React Native project from scratch. This works for both Expo managed workflow and bare React Native projects.

Step 1: Installation & Configuration

Terminal
# Install EAS CLI globally
npm install -g eas-cli

# Login to your Expo account
eas login

# Initialize EAS in your project
eas init

# Configure updates in app.json or app.config.js
{
  "expo": {
    "runtimeVersion": {
      "policy": "appVersion"
    },
    "updates": {
      "url": "https://u.expo.dev/your-project-id",
      "checkAutomatically": "ON_LOAD"
    }
  }
}

Step 2: Wrap Your App with expo-updates

TypeScript — App Entry Point
import { useEffect, useState } from 'react';
import { View, Text, Button, ActivityIndicator } from 'react-native';
import * as Updates from 'expo-updates';

export default function App() {
  const [isUpdating, setIsUpdating] = useState(false);
  const [updateAvailable, setUpdateAvailable] = useState(false);

  useEffect(() => {
    checkForUpdates();
  }, []);

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

  const downloadAndApplyUpdate = async () => {
    setIsUpdating(true);
    try {
      await Updates.fetchUpdateAsync();
      await Updates.reloadAsync(); // Applies update immediately
    } catch (error) {
      console.error('Error applying update:', error);
      setIsUpdating(false);
    }
  };

  if (isUpdating) {
    return (
      
        
        Updating app...
      
    );
  }

  return (
    
      Your App Content
      {updateAvailable && (
        

Step 3: Publish an OTA Update

Terminal
# Publish to preview channel (for internal testing)
eas update --channel preview --message "Fix login bug"

# After testing, publish to production
eas update --channel production --message "Fix login bug v2"

# Gradual rollout — start with 10% of users
eas update --channel production --message "New checkout flow" --rollout-percentage 10

# Increase rollout after monitoring
eas update:edit  --rollout-percentage 50
eas update:edit  --rollout-percentage 100

Step 4: Automatic Update on App Launch (Silent Updates)

TypeScript — Silent Background Update
import { useEffect } from 'react';
import * as Updates from 'expo-updates';

export default function App() {
  useEffect(() => {
    const checkForSilentUpdate = async () => {
      try {
        // Check silently on app launch
        const update = await Updates.checkForUpdateAsync();
        
        if (update.isAvailable) {
          // Download in background without blocking UI
          await Updates.fetchUpdateAsync();
          
          // Apply on next restart (default behavior)
          // Or force immediate reload for critical fixes:
          // await Updates.reloadAsync();
        }
      } catch (error) {
        // Silently fail — app works with current bundle
        console.log('Update check failed, using current version');
      }
    };

    checkForSilentUpdate();
  }, []);

  return ;
}

Best Practice: The default behavior (download in background, apply on next cold start) is recommended for most apps. It ensures fast startup times while keeping users on the latest version. Only force immediate reload for critical security fixes or crashes. citeweb_search:10#2

Step 5: Fingerprint-Based Native Change Detection

One of EAS Updates' most powerful features is automatic detection of native code changes. This prevents the #1 cause of OTA failures: shipping a JavaScript update that requires native modules the user's app doesn't have.

Terminal — Fingerprint Check
# Compare current project against a production build
eas fingerprint:compare --build-id 

# If fingerprints match → safe to ship OTA
# If fingerprints differ → native code changed, need new build
CI/CD — Automated Fingerprint Workflow
name: Deploy to Production
on:
  push:
    branches: ['main']

jobs:
  fingerprint:
    name: Check fingerprint
    type: fingerprint
    environment: production

  get_build:
    name: Check for existing build
    needs: [fingerprint]
    type: get-build
    params:
      fingerprint_hash: ${{ needs.fingerprint.outputs.android_fingerprint_hash }}
      profile: production

  build:
    name: Build (if native changed)
    needs: [get_build]
    if: ${{ !needs.get_build.outputs.build_id }}
    type: build
    params:
      platform: android
      profile: production

  update:
    name: OTA Update (if native unchanged)
    needs: [get_build]
    if: ${{ needs.get_build.outputs.build_id }}
    type: update
    params:
      channel: production

OTA Best Practices: Safe Deployments at Scale

Shipping OTA updates safely at scale requires more than the technical setup. These practices apply regardless of which OTA tool you use. citeweb_search:10#1

1. Never Deploy to 100% at Once

The single most important rule in OTA deployment: always start with a staged rollout. Push to a small percentage of your production user base first — 5% or 10% is a reasonable starting point. Monitor for installs, crashes, and error rates before expanding. Production environments surface issues that staging doesn't, and catching a problem at 5% is dramatically less costly than catching it at 100%. citeweb_search:10#1

2. Keep Staging and Production Channels Separate

Your staging channel should mirror production as closely as possible, but be accessible only to your internal team. Validate every update in staging before it touches a single production user. This isn't optional — it's the practice that separates teams who trust their OTA workflow from teams who treat every push as a gamble. citeweb_search:10#1

3. Have Your Rollback Plan Ready Before You Need It

A rollback reverts your production user base to the previous stable bundle. You should know exactly how to trigger one before you ever need it, and you should have tested the rollback process in staging. Teams that haven't pre-tested rollback tend to scramble when something goes wrong — which is the worst possible time to be figuring it out. citeweb_search:10#1

Terminal — EAS Updates Rollback
# Roll forward with a fix (often faster than rollback)
eas update --channel production --message "Fix crash in checkout"

# Or rollback to previous version
eas update:rollback

# Republish a known-good version
eas update:republish --channel production --destination-channel production

4. Monitor Post-Deploy, Not Just Pre-Deploy

After pushing an update, watch your analytics dashboard. You want to see installs progressing steadily, error rates staying flat or improving, and version distribution shifting toward the new bundle. Any unexpected deviation from normal metrics is a signal to pause and investigate before expanding the rollout. citeweb_search:10#1

5. Document Your Process

Who has permission to push to production? What does a staged rollout look like step by step? What triggers a rollback? These should be written down and accessible to your team, not stored only in someone's head. citeweb_search:10#1

AdSense In-Article Ad — 336x280 / Responsive

Security: Signing, HTTPS & SSL Pinning

OTA updates are powerful — but unmanaged, they introduce real security risks. Unauthorized bundles can compromise your app. Here's how production teams mitigate these risks. citeweb_search:10#5

1. Digitally Sign All Updates

Digital signatures ensure the authenticity and integrity of OTA-delivered code. Sign update bundles with a private key, while client applications validate the signature using the corresponding public key before applying any changes. This prevents the installation of tampered or spoofed updates. citeweb_search:10#5

2. Use HTTPS and TLS for All Communication

All communication between the client and server should occur over HTTPS with modern TLS protocols. This encrypts data in transit and mitigates man-in-the-middle attacks. Implementing SSL Pinning enhances security by allowing the app to trust only specific SSL certificates. citeweb_search:10#5

3. Secure Key Storage

Sensitive assets like private keys and credentials must be stored securely. On iOS, use the Keychain; on Android, use Keystore. These native storage solutions offer robust protection against unauthorized access. citeweb_search:10#5

4. Obfuscate Your JavaScript Code

If you're using the Hermes JavaScript engine, additional obfuscation might not be necessary — Hermes compiles JavaScript into bytecode by default, making it inherently more difficult to read and manipulate. citeweb_search:10#5

CI/CD Automation for OTA Updates

Manual OTA updates don't scale. The best teams automate their entire deployment pipeline — from code push to production rollout — with CI/CD integration.

GitHub Actions + EAS Updates Workflow

YAML — .github/workflows/ota-deploy.yml
name: OTA Deploy to Production

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test
      - run: npm run lint

  fingerprint-check:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}
      - run: eas fingerprint:compare --build-id ${{ secrets.PROD_BUILD_ID }}

  deploy-preview:
    needs: fingerprint-check
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}
      - run: eas update --channel preview --message "${{ github.event.head_commit.message }}"

  deploy-production:
    needs: deploy-preview
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}
      - run: |
          eas update --channel production \
            --message "${{ github.event.head_commit.message }}" \
            --rollout-percentage 10
Free Tool

🚀 Get Our OTA Deployment Playbook

Download our complete CI/CD templates for GitHub Actions, GitLab CI, and Bitrise — including fingerprint checks, staged rollouts, automatic rollback triggers, and Slack notifications. Used by 200+ React Native teams shipping daily updates.

Download Free Playbook

Conclusion: The Future of Mobile Updates

The retirement of CodePush in 2025 was a wake-up call for the React Native ecosystem. OTA updates are not a luxury feature — they're a critical infrastructure component that demands a reliable, actively maintained platform.

In 2026, EAS Updates has emerged as the default choice for most teams. Its combination of Hermes bytecode diffing, fingerprint-based native change detection, phased rollouts, and Expo ecosystem integration makes it the most robust OTA solution available. For teams with existing CodePush integrations, Revopush offers the fastest migration path with SDK compatibility and managed infrastructure.

The principles of safe OTA deployment haven't changed: never ship to 100% at once, always have a rollback plan, monitor post-deploy metrics, and document your process. The tools have evolved, but the fundamentals remain the same.

The teams shipping most confidently in 2026 all follow similar patterns: they test on preview before production, use fingerprint to detect native changes, understand what can and can't be updated over-the-air, and use gradual rollouts with rollback capability. None of these require complex tooling — they're small adjustments that compound over time into fewer incidents, faster iteration cycles, and more confidence in your deployment pipeline.

"The goal isn't perfect deploys. It's recoverable deploys. When something breaks — and it will — you want to contain the blast radius and fix it in minutes, not hours."

Your Next Step: If you're still on CodePush or have no OTA solution, migrate to EAS Updates this week. The setup takes 1–2 days, and the first time you ship a critical fix in 2 minutes instead of 3 days, you'll wonder how you ever lived without it.

Key technical paths

Choose your major
ads here