DEV Community

Cover image for Expo, EAS, Prebuild, OTA, CI/CD — A Complete Mental Model for Modern React Native Development
Suman Bhattarai
Suman Bhattarai

Posted on

Expo, EAS, Prebuild, OTA, CI/CD — A Complete Mental Model for Modern React Native Development

If you used React Native 5–6 years ago, your setup probably looked like this:

  • React Native CLI
  • Fastlane for CI/CD
  • CodePush for OTA updates
  • Manual signing, scripts, and a lot of DevOps glue

Expo existed back then — but it wasn’t trusted for serious production apps.

Fast forward to today, and the ecosystem looks very different.

Expo is now widely adopted and powers production apps at scale. But the mental model around Expo, EAS Build, Prebuild, Dev Client, OTA updates, and CI/CD is still confusing for many developers.

This post connects all the dots — clearly, practically, and honestly.


The Two Layers of Every Expo App

Every Expo / React Native app has two layers.

1) JavaScript Layer

  • React components
  • Business logic
  • Navigation
  • API calls
  • UI
  • Assets (images, fonts)

This layer can be updated OTA.

2) Native Layer

  • iOS (Xcode project, Pods, Info.plist)
  • Android (Gradle, AndroidManifest.xml)
  • Native SDKs (Firebase, Ads, Payments)

This layer cannot be updated OTA. Any change here requires a new build.

Everything Expo does is about managing the boundary between these two layers.


Expo Go: The Starting Point

Expo Go is a prebuilt app from the App Store / Play Store.

What it does:

  • Contains a fixed set of native modules
  • Downloads and runs only your JavaScript code

This is why Expo Go feels magical early on.

But there’s a hard limit:

You can only use native features that Expo Go already includes.

The moment you need:

  • Firebase
  • Google Ads
  • Custom native SDKs

👉 Expo Go is no longer enough.


Expo build architecture

Development Build (Expo Dev Client): Your Custom Expo Go

A Development Build is:

Your own version of Expo Go, built specifically for your app.

It includes:

  • Your required native SDKs
  • Expo dev tools
  • Live reload
  • Debug menu
  • Metro bundler support

Create it with:

eas build --profile development
Enter fullscreen mode Exit fullscreen mode

Run it with:

npx expo start --dev-client
Enter fullscreen mode Exit fullscreen mode

From this point onward, you are no longer limited by Expo Go, but you still enjoy the Expo developer experience.


What Is Expo Prebuild (and Why It Exists)

expo prebuild does one critical thing:

Converts your Expo configuration into real iOS and Android projects.

It generates:

  • ios/
  • android/

Based on:

  • app.json / app.config.js
  • Installed packages
  • Config plugins

Prebuild does not mean leaving Expo. It simply makes the native layer visible.


CNG: Continuous Native Generation

Expo’s recommended approach today is CNG.

Native folders are generated artifacts, not the source of truth.

The source of truth should be:

  • app.json
  • app.config.js
  • Config plugins

Benefits of CNG:

  • Reproducible builds
  • Easier upgrades
  • Safe regeneration
  • Team consistency

If you manually edit native files and rerun prebuild, those changes can be lost. The correct fix is to move those changes into config plugins so they’re reapplied automatically during generation.


EAS Build: CI/CD Without the Pain

The old world of React Native involved:

  • Fastlane
  • Ruby scripts
  • Manual certificate management
  • Store APIs

Modern Expo replaces that with:

  • EAS Build
  • EAS Submit

EAS Build can:

  • Generate native projects automatically
  • Build iOS and Android apps
  • Sign binaries
  • Produce installable artifacts
  • Run in the cloud or locally

Cloud Build vs Local Build

Cloud Build Local Build
Uses Expo servers Uses your machine
No native folders visible Native folders visible
Highly reproducible Environment dependent
CI/CD friendly Debug friendly

A common misconception is that “EAS Build and Prebuild are the same.” They’re not. Prebuild generates native projects; EAS Build compiles and signs a real app. In cloud builds, EAS may generate native projects behind the scenes during the build step.


Debug APK vs Development Build

These are often confused but not the same.

Debug APK (from local run)

If you do local development with prebuild + run, like:

npx expo prebuild
npx expo run:android
Enter fullscreen mode Exit fullscreen mode

You’ll get a debug build (for example a debug APK on Android). It is:

  • Built locally
  • Signed with the debug keystore
  • Installed directly on device/emulator
  • Not meant for sharing
  • Not store-safe

Development Build (EAS)

A development build produced by EAS is:

  • Properly signed
  • Shareable with testers
  • CI/CD friendly
  • Behaves closer to a real distribution build
  • Still supports dev tools and Metro

They may feel similar in day-to-day usage (both connect to Metro, both support dev menu), but the pipeline and signing model are different.


OTA Updates: Expo Updates vs CodePush

Expo provides OTA updates through expo-updates (the module inside your app) plus the EAS Update service (the server side).

expo-updates (the engine)

  • Native module inside your app
  • Downloads JavaScript updates
  • Applies them safely
  • Uses runtimeVersion to prevent incompatible updates

Publishing updates

Older Expo workflows used expo publish, but modern Expo uses EAS Update, typically:

  • eas update

OTA updates can change

  • ✅ UI
  • ✅ Business logic
  • ✅ Bug fixes
  • ✅ Assets

OTA updates cannot change

  • ❌ Native code
  • ❌ Permissions
  • ❌ Native SDKs

If native changes are required, a new build is mandatory.


Channels: Controlled Rollouts

Channels decide who receives an update.

Typical channels:

  • development
  • preview
  • production

Publishing to a channel looks like:

eas update --channel production --message "Fix login bug"
Enter fullscreen mode Exit fullscreen mode

Only builds that are configured to listen to production will receive it.

Channels enable:

  • Staged rollouts
  • QA validation
  • Emergency hotfixes
  • Safe experimentation

EAS Update History & Rollbacks

You can inspect update history from the CLI:

eas update:list
eas channel:list
eas channel:view production
Enter fullscreen mode Exit fullscreen mode

Note: if you haven’t published any updates yet, listing may show no branches/history. Publishing your first update creates the history.


Why Fastlane & CodePush Faded Away

They didn’t disappear because they were bad. They faded because Expo productized the same ideas.

Legacy Tool Expo Equivalent
Fastlane EAS Build / Submit
CodePush EAS Update (expo-updates + hosted updates)

Same concepts — more integrated developer experience.


When Expo Is Not Enough

Expo may not be ideal if:

  • You rely on heavy custom native SDKs
  • You are building games or AR engines
  • You need deep custom native UI frameworks

Even then, you can often start with Expo, use prebuild, and gradually move closer to bare React Native. Expo is a spectrum, not a lock-in.


Final Mental Model

  • Expo Go → run JavaScript only
  • Development Build → your custom Expo Go with your native SDKs
  • Prebuild → generate native projects (ios/android)
  • CNG → config is the source of truth; native folders can be regenerated
  • EAS Build → CI/CD and signing
  • EAS Update → OTA JavaScript updates

Once this clicks, Expo stops being magic — and becomes predictable engineering.


Closing Thoughts

Expo today is not a beginner tool. It is a modern mobile platform that:

  • abstracts DevOps
  • preserves flexibility
  • scales to production
  • respects native boundaries

Understanding the architecture lets you confidently decide:

  • when to rebuild
  • when to ship OTA
  • when to prebuild
  • when to go bare

That’s the difference between using Expo and engineering with Expo.

Top comments (0)