Expo Dev Client vs Expo Go in 2026: Stop Fighting Your Own Tooling
Expo Go is great until it isn't. Here's when to graduate to a custom dev client, what breaks during the switch, and how to keep your team productive while you migrate.

Every React Native team using Expo hits the same wall eventually: you want to add a library that needs native code, you open the docs, and Expo Go quietly stops being an option. Most teams panic, some eject prematurely, and a few discover the middle path — a custom dev client — six months too late.
This is the breakdown we wish someone had handed us the first time a client asked for Bluetooth, HealthKit, or a vendor SDK that ships its own .aar.
What Expo Go actually is (and isn't)
Expo Go is a pre-built sandbox app that Expo publishes to the App Store and Play Store. It bundles a fixed set of native modules — the Expo SDK — and runs your JavaScript on top. That's the entire trick. When you scan a QR code on a teammate's machine, you're loading their JS bundle into the same shared host.
This is brilliant for prototypes, classroom demos, and the first six weeks of almost any greenfield project. There's no Xcode, no Android Studio, no provisioning profiles. A designer can run the app on their iPhone in two minutes.
The ceiling shows up the moment you need any of the following:
- A native module outside the Expo SDK (think: a specific payments SDK, an OEM-provided scanner library, MDM hooks)
- A config plugin that modifies
Info.plistorAndroidManifest.xmlbeyond what Expo Go already declares - Custom entitlements: associated domains, app groups, HealthKit, CarPlay
- A different bundle identifier per environment (staging vs prod) so you can install both on one device
Expo Go can't help with any of those. It's not a limitation of Expo the framework — it's a limitation of running inside someone else's app shell.
What a custom dev client actually is
A dev client is your own app — your bundle ID, your icon, your native modules — built once and reused for every JS change after that. It still has the Expo developer menu, still hot reloads, still connects to Metro. It just happens to be an app you own rather than one Expo publishes.
You build it with EAS Build (or locally if you enjoy pain), install the resulting .ipa or .apk on your devices, and from then on your iteration loop looks almost identical to Expo Go. The difference: you can put whatever native code you want inside it.
The mental model shift
The thing that trips teams up is the separation between native build and JS bundle. With Expo Go they were always coupled in your head — one app, one experience. With a dev client:
- The native binary changes only when you add or update native dependencies
- The JS bundle changes every time you save a file
- Most days, nobody rebuilds the binary at all
In our experience, a healthy team rebuilds the dev client maybe once every two or three weeks. The rest of the time, new engineers install the existing build from an internal distribution link and start hacking on JS the same afternoon.
When to switch — and when not to
Stay on Expo Go if all of these are true:
- You're prototyping or in the first month of a project
- Your dependencies are all inside the Expo SDK
- You don't need separate staging and production installs on the same device
- Your client demo audience is technical enough to install Expo Go themselves
Switch to a dev client when any of these become true:
- A product requirement forces a native module Expo Go doesn't ship
- You need TestFlight or internal Play tracks for non-technical stakeholders
- You want to enforce code signing, certificate pinning, or proguard rules
- Your CI is producing release builds and you want dev and release to share the same native foundation
The mistake we see most often is teams flipping the switch too late, usually the week before a milestone, then losing four days to a build environment they've never touched.
The migration, step by step
Assume you already have an Expo project that runs in Expo Go. Here's the path we use.
1. Install the dev client package
npx expo install expo-dev-client
That's it for the JS side. The package wires up the dev menu and the launcher screen you'll see when you open the app without Metro running.
2. Configure EAS
If you haven't already:
npm install -g eas-cli
eas login
eas build:configure
This creates eas.json. Add a development profile that produces a debuggable build with the dev client baked in:
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": { "simulator": false },
"android": { "buildType": "apk" }
},
"preview": {
"distribution": "internal",
"channel": "preview"
},
"production": {
"channel": "production"
}
}
}
A second development-sim profile with "simulator": true is worth adding so engineers on Macs can run the iOS dev client without a physical device.
3. First build
eas build --profile development --platform ios
eas build --profile development --platform android
The first iOS build will ask about credentials. Let EAS manage them unless your client has strict signing policies — then you'll upload your own .p12 and provisioning profile. Android is more forgiving; EAS will generate a keystore if you don't have one, and you should download it and store it somewhere safe immediately.
4. Distribute
For internal distribution, EAS gives you a QR code and install URL. On iOS, devices need to be registered with your Apple Developer account (UDIDs added to the provisioning profile). On Android, anyone with the link can install.
For anything beyond a small dev team, push the build to TestFlight or Play Internal Testing instead. Less friction, no UDID dance.
5. Daily workflow
Once installed, the loop is the same as Expo Go:
npx expo start --dev-client
The app shows a launcher with recently used Metro URLs. Tap one, you're in. Shake the device for the dev menu. Fast Refresh works. Everything you remember from Expo Go is still there.
The pitfalls nobody warns you about
Native module updates require new builds
Upgrading react-native-reanimated, react-native-screens, or anything with a native component means a new dev client build. Communicate this to your team or you'll get "why is the app crashing on my machine" messages every week.
We pin native dependency versions and bump them deliberately, with a Slack heads-up: "new dev client at this link, install before Monday."
Config plugin order matters
If you use multiple config plugins that modify the same native file, the order in app.json determines who wins. We've debugged a missing NSCameraUsageDescription for an hour before realising a later plugin was overwriting it.
iOS simulator builds and device builds aren't interchangeable
You need separate builds for simulator (x86_64/arm64-sim) and physical devices. EAS handles this if you have separate profiles. If you forget, you'll waste 20 minutes wondering why the .app won't install on your iPhone.
Don't ship the dev client to the store
Production builds should use a separate profile without developmentClient: true. Apple will reject anything with the dev launcher visible, and even if they didn't, you don't want testers shaking the device into your debug menu.
What about plain bare React Native?
There's a third option people forget: a bare React Native app with the Expo modules library installed selectively. You get full native control, you can still use expo-image, expo-router, and friends, but you lose the managed workflow conveniences (auto-generated native projects, simple app.json configuration).
We reach for bare RN when a project has a large existing native codebase to integrate — for example, an enterprise app where the client already has Swift modules they want to keep. For greenfield work, managed Expo with a dev client wins almost every time.
If you're weighing this against fully native Swift or Kotlin, our mobile development services page covers when we'd recommend each path. Short version: native still wins for graphics-heavy apps, deep OS integration, or when you only need one platform.
Where we'd start
If you're on Expo Go today and feeling the squeeze, do this on Monday morning: add expo-dev-client, run eas build:configure, and kick off a development build for both platforms before lunch. By afternoon you'll have a binary you can install, and from that point your team's velocity goes up — not down.
The switch feels intimidating because it crosses the line from "managed" into "you own the build." In practice, EAS handles 90% of what used to be the painful parts. The remaining 10% — credentials, plugin order, dependency discipline — is the price of admission for a real production app, and you were going to pay it sooner or later anyway.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading
Deep Linking in React Native 2026: Universal Links, App Links, and the Edge Cases That Bite
Deep linking looks trivial until a marketing campaign goes live and half your users land on the App Store instead of the screen you promised. Here's what actually works in 2026.

Background Tasks on React Native in 2026: What Actually Runs on iOS and Android
Background execution is the swampiest part of mobile. Here's what actually works on iOS and Android in 2026, what Expo gives you, and where you still need a native module.

React Native New Architecture in 2026: When to Migrate and When to Wait
The New Architecture is the default in fresh React Native projects, but legacy apps face a real migration cost. Here's how we decide when to pull the trigger and when to delay.
