Expo Development Workflow Guide
Managed to Bare Workflow Transition Lessons
Expo Development Workflow Guide
The Prebuild Mistake
I accidentally overwrote my entire iOS configuration because I didn’t understand when to run expo prebuild
. If you’re working with Expo and native libraries, this might help you avoid the same mistake.
It started when I was integrating Google Sign-In with Supabase authentication. What followed was learning about Expo’s different development workflows and when to use each command.
Managed vs Bare Workflow
Expo has two development approaches:
Managed Workflow
- Everything works in Expo Go
- Use
expo install
for packages - No access to native code
- Limited to Expo SDK packages
Bare Workflow
- Full access to
ios/
andandroid/
directories - Can use any React Native library
- Requires building apps locally or with EAS
- More control, more complexity
Understanding expo prebuild
The expo prebuild
command does three things:
expo prebuild
- Generates
ios/
andandroid/
directories from yourapp.json
- Transitions from Managed to Bare Workflow
- Overwrites any existing native code
That third point is important. If you’ve manually edited files in ios/
or android/
, prebuild
will replace them with generated versions.
When to Use prebuild
Safe to use:
- After
expo install
of native libraries - When changing
app.json
settings - Initial transition to Bare Workflow
Don’t use:
- After manually editing native iOS/Android code
- Just to “refresh” things
Development Commands
expo run:android / expo run:ios
expo run:android
This command:
- Builds the native project (Android Studio/Xcode)
- Installs the app on device/emulator
- Starts Metro bundler
- Connects everything for hot reloading
It’s not just a dev server - it’s a full native build.
Development Flow
# Initial build or after native changes
expo run:android
# For JS-only changes
expo start
# Native code changed? Build again
expo run:android
You don’t need to run expo run:android
for every JavaScript change.
Config Plugins
Config Plugins automatically configure native projects during prebuild
. They modify AndroidManifest.xml
, Info.plist
, and other native files.
Example plugin:
// withMyCustomConfig.js
const { withAndroidManifest, withInfoPlist } = require("@expo/config-plugins");
const withCustomAndroidManifest = (config) => {
return withAndroidManifest(config, (config) => {
const manifest = config.modResults;
// Modify manifest here
return config;
});
};
const withCustomInfoPlist = (config) => {
return withInfoPlist(config, (config) => {
config.modResults.MyCustomKey = "MyCustomValue";
return config;
});
};
module.exports = (config) => {
config = withCustomAndroidManifest(config);
config = withCustomInfoPlist(config);
return config;
};
Add to app.json
:
{
"expo": {
"plugins": ["./withMyCustomConfig.js"]
}
}
Config Plugins ensure consistent native configuration every time you run prebuild
.
Google Sign-In Setup
Here’s the actual setup process for Google OAuth:
1. Google Cloud Console Setup
You need three client IDs:
- Web client ID (for Supabase backend)
- iOS client ID
- Android client ID
2. Environment Variables
React Native can’t read .env
files directly. Install react-native-dotenv
:
npm install react-native-dotenv
Configure Babel:
// babel.config.js
module.exports = {
presets: ["module:metro-react-native-babel-preset"],
plugins: [
[
"module:react-native-dotenv",
{
moduleName: "@env",
path: ".env",
},
],
],
};
Usage:
import { GOOGLE_CLOUD_CLIENT_ID_IOS } from "@env";
3. GoogleSignin Configuration
GoogleSignin.configure({
iosClientId: "iOS-CLIENT-ID.apps.googleusercontent.com",
webClientId: "WEB-CLIENT-ID.apps.googleusercontent.com", // iOS needs this too
});
iOS requires both iosClientId
and webClientId
for different auth flows.
4. iOS URL Scheme
Configure in app.json
:
{
"expo": {
"ios": {
"bundleIdentifier": "com.yourapp.identifier"
},
"scheme": "com.yourapp.identifier"
}
}
This allows Google to redirect back to your app after authentication.
Build Process
iOS Build
cd ios
pod install # Install CocoaPods dependencies
cd ..
expo run:ios
Android Build
cd android
./gradlew clean # Clear build cache
cd ..
expo run:android
The gradlew clean
step prevents cached build issues.
Expo Go vs Dev Client
Once you use native libraries, Expo Go stops working. You’ll see “This library is not supported in Expo Go” errors.
Solution is expo-dev-client
:
npx expo install expo-dev-client
expo run:android # Builds custom dev client
Dev Client is a custom version of Expo Go that includes your native dependencies.
Comparison:
- Expo Go: Quick testing, limited libraries
- Dev Client: All libraries, requires building
EAS Build vs Local Builds
Local Builds
expo run:android # Debug build
expo run:android --variant release # Release build
Pros: Fast, full control Cons: Requires local setup, platform-specific
EAS Build
eas build --platform android
eas build --platform ios
Pros: Build iOS on any platform, consistent environment Cons: Requires internet, build queues
Common Issues
1. Prebuild Overwrites Native Code
Problem: Lost custom native modifications
Solution: Only use prebuild
for config changes, not after manual native edits
2. OAuth Test Users
Problem: Google sign-in works in development but fails in production Solution: Add test users in Google Cloud Console OAuth consent screen
3. Environment Variables Not Loading
Problem: Variables undefined at runtime
Solution: Ensure react-native-dotenv
is properly configured in babel.config.js
4. Build Cache Issues
Problem: Changes not appearing in builds Solution: Clean builds:
# Android
cd android && ./gradlew clean && cd ..
# iOS
cd ios && rm -rf build && cd ..
Deployment
Build for Stores
eas build --platform android --profile production
eas build --platform ios --profile production
Submit to App Stores
eas submit --platform android
eas submit --platform ios
Over-the-Air Updates
For JavaScript-only changes:
eas update
Key Takeaways
- Understand your workflow: Managed vs Bare determines which commands work
- Respect prebuild: It overwrites native modifications
- Clean builds solve most problems: When in doubt, clean and rebuild
- Dev Client replaces Expo Go: Once you use native libraries
- Environment variables need setup: React Native doesn’t read
.env
automatically
The transition from Managed to Bare Workflow isn’t just adding folders - you’re taking on native development responsibilities. But the flexibility you gain makes it worthwhile for most real-world apps.