skip to content

In Expo, what is the difference between Expo Go and a development build, and when does a project outgrow Expo Go?

level: juniorimportance: must knowfreq 68%

answer

  1. prebuilt store app versus your own binary
  2. fixed set of native libraries
  3. one SDK version at a time
  4. expo-dev-client adds the launcher
  5. remote push, links, icons need your binary

basics

~20 s

Expo Go is a prebuilt app with a fixed set of native libraries for one SDK. A development build is your own debug binary with expo-dev-client, holding your native code and config. Outgrow Expo Go when you need native code it lacks.

solid answer

~40 s

Expo Go is a sandbox app from the stores: its native side is compiled once, with a fixed set of native libraries, for a single Expo SDK version, and it runs whatever JavaScript bundle your dev server sends. A development build is a Debug build of **your** app that includes `expo-dev-client`, so it contains exactly your native modules, app name, icon, URL schemes and permissions, plus a launcher and dev menu. JavaScript still reloads from Metro in both. You outgrow Expo Go when you need a native library it does not bundle, a config plugin, your own icon or splash behaviour, remote push notifications, universal or app links, or an older SDK on an iPhone. Most production apps move to development builds early.

go deeper

for a junior

Recall that Expo Go is a prebuilt app with fixed native code for one SDK, while a development build is your own app with expo-dev-client.

for a middle

Explain the native-app versus JavaScript-bundle split and use it to predict which features fail in Expo Go and when a rebuild is needed.

for a senior

Show when you move a team off Expo Go: list the triggers, plan shared development builds, and set a rule for when native changes force a rebuild.

for a principal

Decide how development builds are produced and distributed for a team, and how that fits the release pipeline and device fleet.

## Two halves of every Expo app An Expo app running in development has two parts: - the **native app** installed on the device, which contains all native code and native configuration; - the **JavaScript bundle**, served by Metro through `npx expo start` and reloaded as you edit. JavaScript can only call native APIs that were compiled into the native app. That single fact explains every difference between Expo Go and a development build. ## Expo Go: a shared, prebuilt native app **Expo Go** is a native app published to the App Store and Play Store. Its native side was built by Expo with a **fixed set of native libraries**, and it cannot be changed after installation. - It supports **one Expo SDK version at a time**; a new SDK release replaces the store version. - On Android devices, emulators and the iOS Simulator you can install an older Expo Go; on an **iPhone** you cannot, because Apple does not allow side-loading older app versions. - It shows a generic splash and cannot test your icon, app name or native splash options. - It runs only the New Architecture. ## A development build: your own native app A **development build** is a Debug build of your project that includes **`expo-dev-client`**. Because you compile it, it contains: - every native module your `package.json` installs; - the native configuration from your app config and config plugins: name, icon, bundle identifier, URL schemes, entitlements, permission strings; - the dev-client **launcher** (choose a dev server or a published update) and an extended **dev menu**. You build it locally with `npx expo run:ios` / `npx expo run:android`, or in the cloud with EAS Build. | | Expo Go | Development build | |---|---|---| | Native code | fixed set chosen by Expo | whatever your project installs | | SDK versions | one, the current one | the one your project uses | | App identity | Expo Go's | yours: name, icon, bundle ID | | Remote push | not available | available with your credentials | | Universal / app links | not possible | configured in your binary | | Needs a native build | no | yes, once per native change | ## Signals that a project has outgrown Expo Go 1. **A native library Expo Go does not include**: the JavaScript loads, then fails as soon as it calls native code that is not there. 2. **A config plugin** that must change `Info.plist`, `AndroidManifest.xml` or entitlements. 3. **Remote push notifications**, which belong to your own push credentials; on Android they were removed from Expo Go in SDK 53. 4. **Universal Links or Android App Links**, which require your domain in the native app. 5. **Testing app identity**: icon, name, splash options. 6. **An older SDK on an iPhone**, which Expo Go cannot provide. ## What stays the same The inner loop is identical: edit JavaScript, Fast Refresh, same Metro. A development build only needs rebuilding when **native** inputs change, such as adding a native library or editing native config, not for JavaScript changes. That is why teams build it once, share it, and iterate on JavaScript for days. ## Where Expo Go still fits - **Learning and prototypes** that use only Expo SDK modules bundled in Expo Go. - **Quick demos** on a colleague's phone without installing a custom build. - **Libraries with JavaScript fallbacks**, which Expo Go can load even when native code is absent. For a production app, the development build is the normal environment, because it behaves like the binary you will ship. ## A typical migration path 1. Install `expo-dev-client` with `npx expo install expo-dev-client`. 2. Produce the first development build, locally with `npx expo run:ios` / `run:android` or on EAS. 3. Install it on every simulator, emulator and device the team uses. 4. Keep running `npx expo start`; with the library installed, its QR code now opens the development build. 5. Agree when the build must be rebuilt (any native change) and how the new build reaches everyone. The JavaScript does not change at all during this move. What changes is where the native half comes from: Expo's shared app before, your own compiled app after.

  • Why does a native library not bundled in Expo Go fail only when its code runs, not when the bundle loads?
    Metro bundles the library's JavaScript like any other module, so the bundle loads. The failure comes when that JavaScript asks for its native module, which was never compiled into Expo Go. Only a native app built with the library contains it.
  • Does a development build need rebuilding every time JavaScript changes?
    No. JavaScript is served by Metro and reloaded with Fast Refresh, exactly as in Expo Go. Rebuild only when native inputs change: a new or updated native library, a config plugin, or native app config such as the name, icon or permissions.
  • Can you install an older Expo Go on an iPhone to open an SDK 56 project?
    No. Expo Go on the App Store supports only the current SDK, and iOS does not allow side-loading older versions. On Android devices, emulators and the iOS Simulator an older Expo Go can be installed; on an iPhone, use a development build.

Expo Go is a rental car: you can drive anywhere immediately, but you cannot fit your own tow bar. A development build is your own car, built to your spec, which takes time to build but carries every part you need.

saying these in an interview costs you the question

  • Expo Go can load any native library once it is in package.json
  • A development build must be rebuilt for every JavaScript change
  • Expo Go supports every past SDK version on an iPhone
  • A development build is the same thing as a release build
  • Expo Go cannot show any notifications at all