"Works perfectly in Rork preview, crashes immediately on my phone" — this is one of the most common issues developers run into when taking a Rork-generated app off the preview platform and onto a real device.
The gap exists because Rork's preview environment handles several things automatically that you need to configure explicitly for a real build: environment variables, native module versions, platform permissions. Once you know these four patterns, most crashes resolve quickly.
Identify the Crash Type First
Different crash patterns point to different causes, so observation saves time:
- Crashes immediately on launch → Missing environment variables or native module initialization failure
- Crashes on a specific action → Bug in that specific code path or API
- Crashes only on iOS (or only Android) → Platform-specific permissions or native API differences
- Works on Expo Go but crashes in a standalone build → Missing native plugin configuration in
app.json
Fix 1: Missing Environment Variables
Rork's preview environment automatically injects environment variables you set in the project settings. A real device build doesn't get those automatically.
When you see this in crash logs:
TypeError: undefined is not an object (evaluating 'process.env.EXPO_PUBLIC_API_KEY.length')
For local testing with Expo Go, create .env.local in your project root:
# .env.local — local device testing
EXPO_PUBLIC_API_KEY=your_actual_api_key_here
EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_hereFor EAS Build (App Store submission), add to eas.json or use the EAS dashboard:
{
"build": {
"production": {
"env": {
"EXPO_PUBLIC_API_KEY": "your_production_api_key"
}
}
}
}Add a guard in your code to get a clear error message instead of a cryptic crash:
const apiKey = process.env.EXPO_PUBLIC_API_KEY;
if (\!apiKey) {
console.error('API key is not configured. Check your .env file.');
// Show an error screen instead of crashing
}Fix 2: Native Module Version Mismatches
Rork-generated code uses Expo SDK packages like expo-camera, expo-location, and expo-notifications. If the versions in package.json don't match what your Expo SDK version expects, the app will crash on launch.
# Check your current Expo SDK version
cat package.json | grep '"expo":'
# Fix all package versions to be compatible with your Expo SDK
npx expo install --fix
# Reinstall the most crash-prone native packages explicitly
npx expo install expo-notifications expo-camera expo-location expo-image-pickerexpo-notifications and expo-camera are the most frequent culprits for version-related crashes. Running npx expo install --fix after cloning or after Rork regenerates the project usually resolves it.
Fix 3: Missing iOS/Android Permission Configuration
If your app uses camera, location, microphone, or notifications, iOS and Android both require explicit permission strings in your native configuration. Rork's generated app.json sometimes leaves these out.
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "Used to take photos in the app",
"NSLocationWhenInUseUsageDescription": "Used to show your location on the map",
"NSMicrophoneUsageDescription": "Used for voice input"
}
},
"android": {
"permissions": [
"CAMERA",
"ACCESS_FINE_LOCATION",
"RECORD_AUDIO"
]
},
"plugins": [
["expo-camera", { "cameraPermission": "Used to take photos" }],
["expo-location", { "locationWhenInUsePermission": "Used to show your location" }]
]
}
}Without these, iOS silently denies the permission request and crashes instead of showing a dialog. On Android, you'll see SecurityException in the logs.
Fix 4: Async Initialization Timing Issues
On a real device, app startup can be slightly slower, which exposes race conditions in async initialization code. If you try to access Supabase or Firebase data before the client has finished initializing, you'll get a crash that never appeared in preview.
// ❌ Risky: fetches data before client might be ready
useEffect(() => {
fetchUserData(); // supabase client might not be initialized yet
}, []);
// ✅ Wait for initialization to complete
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const initialize = async () => {
await supabase.auth.getSession(); // Ensure client is ready
setIsReady(true);
};
initialize();
}, []);
if (\!isReady) {
return <LoadingScreen />;
}Reading Crash Logs
For crashes you can't reproduce easily, logs are your best tool.
iOS — Open Xcode, go to Window → Devices and Simulators, select your device, click View Device Logs. Filter by your app bundle ID.
Android — With the device connected via USB:
adb logcat *:E | grep "ReactNative\|expo\|your.app.bundle"Expo Go — Shake the device to open the debug menu, enable Remote JS Debugging, then open Chrome's DevTools for live error output.
Start Here
When an app crashes on device but works in Rork preview, run through this in order:
- Check that all environment variables are configured for the real build environment
- Run
npx expo install --fixto sync package versions - Verify
app.jsonhas permission strings for every native feature you're using - Read the device crash logs to get the actual error message
These four steps cover the vast majority of Rork-generated app crash scenarios. The patterns repeat across projects, so after hitting each one once, you'll recognize them immediately going forward.