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.
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
Run it with:
npx expo start --dev-client
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
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"
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
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)