What each one actually is
Expo Go is one app, published by Expo, that you install from the App Store or Play Store. It contains a React Native runtime plus a fixed, precompiled set of Expo SDK native modules. When you scan a QR code, it downloads your JavaScript bundle and runs it against those modules. Your project never gets compiled, which is exactly why it starts in seconds, and exactly why it cannot run native code Expo did not anticipate.
A development build is the same idea with the binary swapped for yours. You add expo-dev-client, build once, and install the result on your device. It still connects to Metro, still hot-reloads, still has a dev menu, but the native side is compiled from your project, so every dependency and config plugin you declared is really in there.
The mental model that helps: Expo Go is someone else’s app running your JavaScript. A development build is your app, in development mode.
Side by side
| Capability | Expo Go | Development build |
|---|---|---|
| Time to first run | Seconds, scan a QR code | One cloud or local build (5–20 min) |
| Hot reload | ✅ | ✅ Identical |
| Expo SDK modules | ✅ The set Expo bundled | ✅ Any of them |
| Third-party native libraries | ❌ | ✅ |
| Config plugins that touch native files | ❌ | ✅ |
| Custom bundle ID / icon / splash on device | ❌ Shows as Expo Go | ✅ Your app |
| Remote push notifications (Android) | ❌ Removed in SDK 53 | ✅ |
| Native debugging in Xcode / Android Studio | ❌ | ✅ |
| Share with a non-technical tester | ⚠️ They install Expo Go first | ✅ Internal distribution link |
| Rebuild needed when JS changes | No | No |
| Rebuild needed when native deps change | N/A | Yes |
The five triggers that end Expo Go
- A native dependency. Payments, health data, Bluetooth, maps with a vendor SDK, a native video player, most analytics SDKs. If the package ships iOS or Android source, Expo Go cannot load it.
- A config plugin. Anything that edits
Info.plist, the Android manifest, entitlements, or build settings. Expo Go runs a binary that was compiled before your plugin existed. - Android push notifications. Remote push in Expo Go on Android was removed in SDK 53. If notifications are core to your product, you need a development build from day one.
- Real testers. Handing a beta to someone who has to install Expo Go, make an account, and scan a code loses you most of the testers. Internal distribution of a development build is a link and an install.
- Anything identity-shaped. Custom bundle identifier, app icon, deep-link scheme, associated domains. In Expo Go the app on the home screen is Expo Go, so none of it is testable, including deep links.
Making the switch
It is a one-time cost of about twenty minutes, most of which is a build queue:
npx expo install expo-dev-client
# eas.json → add a development profile:
# "development": {
# "developmentClient": true,
# "distribution": "internal"
# }
npx eas build --profile development --platform ios
# or build locally, no EAS account needed:
npx expo run:ios
npx expo start # opens in your dev build, not Expo GoAfter that, day-to-day work is identical to Expo Go. You only rebuild when the native layer changes, which is rarer than people expect, adding a screen, changing state management, restyling everything, and rewriting your API layer are all pure JavaScript.
One habit worth adopting: rebuild the development client deliberately, right after adding a native dependency, rather than the next time something breaks. Half of “Expo is flaky” complaints are a stale dev client running new JavaScript.
So which should you use?
Use Expo Go for the first few days of a project, for learning, and for throwaway prototypes where the entire app is Expo SDK modules and JavaScript.
Use a development build for anything you intend to ship. If you already know the app will take payments or send notifications, skip Expo Go entirely, the twenty minutes you save is repaid with interest the first time you debug a phantom crash.
Skipping the setup entirely
If you would rather not hand-configure any of this, ShipNative generates a complete Expo project from a description, runs it live on your phone while you iterate, and exports the source with EAS configuration already in place, so the first time you touch eas.json is when you want to change something, not to get started.