I wanted to add a WidgetKit extension to one of my wallpaper apps, so I opened the Expo project that Rork had exported in Xcode and installed a Release build onto my phone. The moment the splash screen disappeared, the screen filled with red text:
No bundle URL present.
Make sure you're running a packager server or have included a .jsbundle file in your application bundle.
It ran fine in the development build, so at first I blamed my own code. The real cause was earlier than that: the JavaScript simply wasn't anywhere the app could find it. You may have seen the same red screen in a Debug build, but Debug and Release fail for completely different reasons. Mix them up and you lose half a day. I did.
What the error is actually telling you
No bundle URL present means the native side of the iOS app has no idea where to load JavaScript from at launch. A React Native / Expo app runs a JavaScript bundle inside a native shell, and that bundle can live in one of two places.
- Debug build: it connects to the Metro bundler running on your Mac at
http://<your-mac-ip>:8081and loads the JS over the network - Release build: it loads from a
main.jsbundlefile baked into the app itself
The red screen means neither of those was found. So the very first thing to determine is whether it happened in Debug or Release.
If it happened in a Debug build — Metro isn't reachable
If your scheme was still set to Debug when you installed onto the device, this is almost always a Metro connection problem. In order of how often I hit each:
- Metro isn't running. Forgetting to start
npx expo startin a terminal is the most common trap of all. - The Mac and the iPhone are on different Wi-Fi networks. A physical device can't reach your Mac via
localhost. Same network is the baseline requirement. - The router blocks device-to-device traffic. Guest SSIDs and "client isolation" settings stop the connection even when both devices show the same network name.
The quick check: open http://<your-mac-ip>:8081/status in Safari on the iPhone. If it returns packager-status:running, the route is alive and the problem is elsewhere. If nothing comes back, it's a network issue.
The most reliable path is to connect the device over USB and reinstall with:
# With the device connected over USB, this forwards the port and launches it
npx expo run:ios --deviceexpo run:ios resolves the device and handles the Metro port for you. On flaky Wi-Fi this is by far the least painful option.
If it happened in a Release build — the bundle wasn't baked in
This is the real subject. A Release configuration never talks to Metro. The app must contain a main.jsbundle, and without it you get the red screen. This is exactly where people who start editing a Rork-exported project locally in Xcode get stuck.
There are two main causes.
1. The bundling build phase didn't run
A React Native iOS project has a shell-script build phase called "Bundle React Native code and images" that packages the JS at build time. A clean project from Expo prebuild includes it, but while hand-editing native code you can accidentally set it to skip, or the script's reference paths can drift, and then no bundle is produced even in Release.
In Xcode, open the target → Build Phases and confirm the Bundle React Native code and images phase exists and that Run script only when installing is unchecked. When it's checked, the script runs only during Archive, not on a normal Run — which produces the confusing symptom "red screen on device, but Archive works fine."
2. You aren't using expo run:ios --configuration Release
Rather than hitting raw xcodebuild or Xcode's Run in Release, it's more reliable to let Expo handle the Release build.
# Let Expo do the Release build, bundle embedding included
npx expo run:ios --configuration Release --deviceThis command generates the bundle and embeds it before installing to the device, so you spend far less time fighting manual build-phase settings.
A stopgap — building the offline bundle by hand
When you just want it running on a device right now, before touching build-phase settings, you can generate the bundle manually and drop it into the project.
# Generate main.jsbundle and assets under the ios directory
npx react-native bundle \
--platform ios \
--dev false \
--entry-file index.js \
--bundle-output ios/main.jsbundle \
--assets-dest iosOn projects using Expo Router, --entry-file may need to be node_modules/expo-router/entry.js. If it complains it can't find the entry, check the main field in package.json. Add the generated main.jsbundle to the target in Xcode (not as a folder reference), and Release builds will read from it.
But treat this strictly as a stopgap. A hand-placed bundle doesn't auto-update when you change code, so once you've fixed the real cause (the build phase), remove the manual bundle. I stepped on this trap a few times and caused a secondary mess — "it worked!" while an old JS bundle was still baked in — so I'll say it plainly.
The order I work through it (when in doubt, follow this)
When I see the red screen, I always knock it down in this order.
- Is the configuration Debug or Release? (check the scheme at the top-left of Xcode)
- If Debug: is Metro running, and are the device and Mac on the same Wi-Fi?
- If Release: does Build Phases include the bundling phase, and is
only when installingunchecked? - Still failing? Rebuild with
expo run:ios --configuration Release --device - If you need a working build faster than a root-cause fix, generate the offline bundle by hand
Following that order alone keeps you from the detour of distrusting your code and reading logs endlessly.
Prevention — so you never lose half a day again
I've run apps with over 50 million cumulative downloads as a solo developer since 2014, and I often split the work: scaffold quickly in Rork, then add only the native pieces — WidgetKit, Live Activities — in Xcode. Once I made it a habit to verify on a real device in Release every time, this error stopped stopping me. Don't conclude "it works" from the dev build alone; install the store configuration (Release) onto a real device at least once. That one habit prevents the panic moments right before review.
Try installing your current project onto a device with npx expo run:ios --configuration Release --device. If no red screen appears there, your distribution build is carrying its bundle correctly. I hope this helps anyone stuck at the same spot.