| name | expo-dev-client |
| description | Use when building custom development clients for Expo apps, integrating custom native modules, using Apple ecosystem features, third-party native SDKs, or when Expo Go limitations are encountered |
Expo Dev Client
Overview
A custom development client is a debug build of your app that includes your specific native modules and configuration. It replaces Expo Go when you need native code that Expo Go does not bundle.
Core principle: Use Expo Go for rapid prototyping. Switch to a dev client when you need custom native code, and use EAS Build to create and distribute it.
When to Use
Use a dev client when your app requires:
- Custom native modules not included in Expo Go (e.g., Bluetooth, NFC, custom camera pipelines)
- Apple ecosystem integration (HealthKit, HomeKit, CarPlay, App Clips)
- Third-party native SDKs (Stripe native, Mapbox, Firebase Analytics with custom config)
- Custom native configuration (specific entitlements, background modes, custom URL schemes)
- Config plugins that modify native project files
- Expo Modules API for writing your own native modules
When NOT to Use
- App only uses libraries included in Expo Go (check the Expo SDK docs)
- Quick prototyping or demo purposes (stick with Expo Go)
- Web-only features
Expo Go vs Dev Client
| Feature | Expo Go | Dev Client |
|---|
| Native code | Pre-bundled only | Any native module |
| Setup time | Zero | Requires build step |
| OTA updates | Yes | Yes |
| Custom config plugins | No | Yes |
| App Store distribution | No | Yes (via TestFlight) |
| Custom app icon/splash | No | Yes |
| Background modes | Limited | Full control |
| Fast Refresh | Yes | Yes |
Setup
1. Install the Dev Client Package
npx expo install expo-dev-client
This adds the development client UI (launcher, error overlay, dev menu) to your app.
2. Configure EAS Build
npm install -g eas-cli
eas build:configure
This creates eas.json:
{
"cli": {
"version": ">= 5.0.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": {
"simulator": true
}
},
"preview": {
"distribution": "internal"
},
"production": {}
}
}
3. Build the Dev Client
eas build --platform ios --profile development
eas build --platform ios --profile development \
--local
eas build --platform android --profile development
4. Install and Run
eas build:run --platform ios
npx expo start --dev-client
Distribution
Internal Distribution (Testing)
For sharing dev builds with your team:
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
}
}
}
Register test devices:
eas device:create
eas build --platform ios --profile development
Team members install via the link provided after build completes.
TestFlight Distribution
For broader beta testing:
{
"build": {
"preview": {
"distribution": "internal",
"ios": {
"buildConfiguration": "Release"
}
}
},
"submit": {
"production": {
"ios": {
"appleId": "your@email.com",
"ascAppId": "1234567890"
}
}
}
}
eas build --platform ios --profile preview
eas submit --platform ios
Local Builds
Build on your machine instead of EAS cloud servers:
eas build --platform ios --profile development --local
Advantages of local builds:
- No queue time
- Full control over build environment
- Access to local secrets/certificates
- No EAS build minutes consumed
Adding Native Modules
With Config Plugins
Most Expo-compatible libraries use config plugins that automatically configure native code:
npx expo install expo-camera
{
"expo": {
"plugins": [
[
"expo-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera"
}
]
]
}
}
Rebuild your dev client after adding plugins:
eas build --platform ios --profile development
Custom Native Modules (Expo Modules API)
npx create-expo-module my-native-module --local
This scaffolds:
modules/
my-native-module/
android/
src/main/java/.../MyNativeModule.kt
ios/
MyNativeModule.swift
src/
index.ts # TypeScript API
expo-module.config.json
Development Workflow
1. npx expo start --dev-client
2. Open dev client app on device/simulator
3. Dev client connects to your local dev server
4. Fast Refresh works for JS/TS changes
5. Native code changes require rebuild
Dev Menu
Shake device or press Cmd+D (iOS simulator) / Cmd+M (Android emulator):
- Reload JS bundle
- Toggle Fast Refresh
- Open debugger
- Performance monitor
- Element inspector
Config Plugins
Config plugins modify native project configuration at build time without ejecting:
import { ConfigPlugin, withEntitlementsPlist } from 'expo/config-plugins';
const withCustomEntitlement: ConfigPlugin = (config) => {
return withEntitlementsPlist(config, (mod) => {
mod.modResults['com.apple.developer.healthkit'] = true;
mod.modResults['com.apple.developer.healthkit.access'] = ['health-records'];
return mod;
});
};
export default withCustomEntitlement;
{
"expo": {
"plugins": ["./plugins/withCustomEntitlement"]
}
}
Common Mistakes
| Mistake | Fix |
|---|
Running npx expo start without --dev-client | Must use --dev-client flag to connect to custom builds |
| Forgetting to rebuild after adding native module | Any native dependency change requires eas build |
| Using Expo Go config with dev client | Remove expo-go specific workarounds |
| Not registering iOS test devices | Run eas device:create before building for physical devices |
| Mixing development and production profiles | Keep eas.json profiles separate and clear |
| Building for simulator when targeting device | Set "simulator": false in the iOS build profile for devices |
Quick Reference
| Task | Command |
|---|
| Install dev client | npx expo install expo-dev-client |
| Configure EAS | eas build:configure |
| Build for iOS simulator | eas build --platform ios --profile development |
| Build locally | eas build --platform ios --profile development --local |
| Start dev server | npx expo start --dev-client |
| Register device | eas device:create |
| Install build | eas build:run --platform ios |
| Submit to TestFlight | eas submit --platform ios |
| Create native module | npx create-expo-module my-module --local |