[r] only when native dependencies change.
For build and upload steps, see Expo Build Guide.
How detection works
The CLI looks for two things in the current directory:"expo"inpackage.jsondependencies or devDependencies- At least one project indicator:
app.json,app.config.js,app.config.ts,eas.json, or.expo/directory
URL schemes
Hot reload deep-links the dev client to your local Metro server via a custom URL scheme:myapp://) registered in the dev client binary.
Where to find your scheme
Checkapp.json under expo.scheme:
app.config.js / app.config.ts:
.revyl/config.yaml field.
Why universal links don’t work
Apps that only use universal links (https://example.com/...) for deep linking cannot use those for hot reload. Apple’s associated domains system requires a live HTTPS domain serving an apple-app-site-association file. Revyl hot reload uses short-lived relay hosts that are not suitable as stable associated domains. Custom URL schemes (myapp://) bypass this entirely — no server verification needed.
No URL scheme in the app
If your app has noexpo.scheme (common in apps that only use universal links), you need to add one. The name is arbitrary and only affects the dev client:
revyl init --detect from
the app directory, edit the generated profile as needed, and run
revyl config validate.
Checking existing schemes without rebuilding
The dev client may already have a scheme registered byexpo-dev-client. Check the iOS build:
exp+myslug, that scheme is already baked into
the client. Build it with revyl build so the artifact carries the same scheme
metadata.
Generated scheme prefixes
Expo dev clients can register URL schemes in two formats:- Base scheme:
myapp://— registered whenexpo.schemeis set in app.json - Prefixed scheme:
exp+myapp://— auto-registered byexpo-dev-clientbased on the app slug
addGeneratedScheme setting
at build time. If the selected build lacks scheme metadata or the registered
scheme is wrong, configure expo.scheme (and addGeneratedScheme when
applicable) in the Expo app and rebuild the development client.
Monorepo setup
In monorepos (Turborepo, Nx, pnpm workspaces), the Expo app typically lives in a subdirectory likeapps/native/, apps/mobile/, or packages/app/.
Run from the Expo app directory
All Revyl commands must run from the directory containing the Expo app’spackage.json — not the monorepo root.
FindRepoRoot, which walks up from the current directory looking for .revyl/. If the monorepo root also has a .revyl/ directory (common for CI test configs), make sure you’re in the correct subdirectory.
Why detection fails in monorepos
Two things go wrong:-
Hoisted dependencies — Package managers like pnpm, Yarn, and npm may hoist
expoto the rootnode_modules/or useworkspace:*protocol in the localpackage.json. The Expo provider reads the localpackage.jsonand checks for"expo"independenciesordevDependencies. If it’s not there, the provider returns nil (no match). -
Native directories match other providers — Every Expo project with prebuild has
ios/containing.xcodeprojfiles (triggering the Swift provider at confidence 0.7) andandroid/containingbuild.gradle(triggering the Android provider at confidence 0.6). When the Expo provider returns nil, these become the top matches.
revyl init detects “Swift/iOS (coming soon)” and “Android (coming soon)” instead of Expo.
Fix: Rerun detection from the Expo app directory:
build.framework and the generated
profile recipes in .revyl/config.yaml, then run revyl config validate. If
expo is genuinely missing from the local package.json, add it:
.revyl/config.yaml should have build.framework: expo and a
named development profile with the applicable iOS and/or Android recipes. The
custom URL scheme remains in the Expo app configuration, not the Revyl YAML.
What to expect when the deep link opens
Afterrevyl dev starts, the CLI opens a deep link to connect the dev client to your local Metro server. You may notice:
- A confirmation dialog — iOS shows “Open in [Your App]?” with Cancel and Open buttons. This is a standard iOS security prompt for URL scheme handoffs. Tap Open to proceed. (On cloud simulators this currently requires a manual tap; future CLI versions will auto-accept it.)
- The app briefly restarts — The dev client may close and reopen as it disconnects from its cached state and reconnects to the tunnel URL. This looks jarring but is normal Expo dev client behavior. The app is reloading to fetch the JS bundle from your local Metro server instead of its built-in bundle.
- Sometimes no restart at all — If the dev client is already in a fresh state (first launch after install), it may connect seamlessly without closing. The behavior varies by Expo SDK version and whether the app was previously running.
Dynamic config (app.config.js / app.config.ts)
Dynamic Expo config must still resolve a custom scheme. Verify the evaluated configuration with Expo tooling. For managed builds, Revyl reads the registered scheme from the compiled app and attaches it as artifact metadata. If inspection is unavailable, the build and upload still succeed, but that artifact cannot start an Expo dev loop until compatible scheme metadata is available. The scheme is the custom URL prefix baked into the Expo dev client, such asmyapp-dev://. Revyl uses it to open the installed dev client on the cloud
device and connect it to Metro for hot reload.
Configure the scheme in app.config.js or app.config.ts, then run detection
from the app directory:
.revyl/config.yaml and run revyl config validate.
If you add or change the scheme, rebuild the Expo dev client once. After that,
JS/TS changes can hot reload through revyl dev without another build.