expo-dev-client turns your debug binary into a proper development environment: a launcher for switching Metro servers, an extensible dev menu, and support for every native module your project installs - the standard path beyond Expo Go on SDK 57.
# Add the library (pins compatible version for SDK 57)npx expo install expo-dev-client# Build and install a development clientnpx expo run:ios # local simulator / USB devicenpx expo run:android --device# - or cloud -eas build --profile development --platform all# Serve JavaScript to the dev client (not Expo Go)npx expo start --dev-client
expo-dev-client is a native module plus a config plugin that wires a launcher UI, deep-link scheme, and enhanced dev menu into debug builds.
Development builds (debug binaries with expo-dev-client) load JavaScript from Metro - same Fast Refresh story as Expo Go, but the native side is your app id, icons, and installed modules.
launchMode controls cold start: most-recent jumps into the last project URL; launcher shows the server picker (useful for QA switching environments).
defaultLaunchURL skips the picker entirely when set - pair with per-platform overrides for Android emulator (10.0.2.2) vs iOS simulator (localhost).
Native changes (new config plugin, expo prebuild, Gradle/Pod edits) require npx expo run:* or eas build; pure JS/TS changes hot-reload via npx expo start --dev-client.
Guard all dev-client APIs with __DEV__ so production release builds tree-shake the calls (the APIs are no-ops in release, but guarding keeps bundles clean).
Starting Metro without --dev-client - CLI may wait for Expo Go or show the wrong QR. Fix: Always npx expo start --dev-client once the project depends on expo-dev-client.
Editing app.json plugin config without rebuilding - Launcher settings are compile-time. Fix: Run npx expo run:* or eas build after plugin changes.
Android emulator cannot reach localhost:8081 - Emulator maps host loopback to 10.0.2.2. Fix: Set android.defaultLaunchURL to http://10.0.2.2:8081 or use --lan.
registerDevMenuItems after hot reload duplicates entries - Items append per registration. Fix: Register once in a module-level if (__DEV__ && !globalThis.__DEV_MENU__) guard.
Expecting Expo Go to load custom native modules - Expo Go ships a fixed binary. Fix: Install your development build; Native module cannot be null means you need a rebuild.
Using development builds for store performance testing - Debug dev clients include extra tooling. Fix: Profile on preview or release builds for FPS and startup metrics.
iOS Simulator Expo Go confusion - Simulator may still have Expo Go installed alongside your dev client. Fix: Open the correct app icon (your app name, not "Expo Go").
A library and config plugin that adds a development launcher, enhanced dev menu, and debugging tools to your own debug app binary - Expo's name for a "development build."
How do I install expo-dev-client on SDK 57?
npx expo install expo-dev-client
Never pin the version manually - expo install resolves the SDK 57-compatible release.
Why must I run expo start --dev-client?
The flag tells Expo CLI to generate URLs and QR codes for your custom binary. Without it, tooling assumes Expo Go is the target host.
What is launchMode most-recent vs launcher?
most-recent opens the last Metro URL directly (falls back to launcher if unreachable).launcher always shows the server picker - better for QA switching branches.
When do I need to rebuild the native app?
After adding/removing native modules, changing config plugins, editing app.json icons/splash/bundle id, or upgrading Expo SDK. Pure JS edits hot-reload without rebuild.
How do I add custom dev menu buttons?
import * as DevClient from "expo-dev-client";await DevClient.registerDevMenuItems([ { name: "My action", callback: () => console.log("run"), shouldCollapse: true },]);
Register once at app startup in __DEV__ only.
How do I open the dev menu without shaking the phone?
Press m in the Metro terminal, call DevClient.openMenu() from JS, or use the three-finger long press on iOS simulators.
Can I use expo-dev-client with Expo Router?
Yes - the default SDK 57 template includes Router. The dev client loads your router entry (app/_layout.tsx) like any other app.
What eas.json profile builds a dev client?
Set "developmentClient": true on a build profile (commonly named development). Run eas build --profile development.
Does expo-dev-client work on simulators?
Yes. Use "ios": { "simulator": true } in the development profile for simulator IPAs, or npx expo run:ios locally.
What URL should Android emulators use for Metro?
http://10.0.2.2:8081 - the emulator's alias for the host machine's localhost. Set android.defaultLaunchURL in the plugin config.
Is expo-dev-client included in production builds?
The native shell is the same project, but dev-client UI is stripped from release builds. Ship eas build --profile production for store binaries.
How is this different from Expo Go?
Expo Go is a shared app with a fixed native module set. A dev client is your app binary with your native dependencies and bundle identifier.
Gotcha: Native module cannot be null - what now?
Install expo-dev-client, rebuild with npx expo run:android or eas build --profile development, install the new binary, then start Metro with --dev-client.