In a real app
In an Expo project, the custom scheme is declared with scheme in app.json, and prebuild injects it into Info.plist (CFBundleURLTypes) and into the intent filter in AndroidManifest.xml. Without prebuild, both edits are manual, plus forwarding to RCTLinkingManager in AppDelegate on iOS, or in SceneDelegate from React Native 0.88. On Android, android:launchMode="singleTask" on MainActivity avoids stacking a new app instance on every link.
Bugs cluster around the “app closed” case: the initial URL is read once, often before navigation is mounted, and a splash or authentication screen can swallow it. You need a single place that stores the pending URL, then navigates once the tree is ready. Expo Router handles this; with React Navigation, linking.getInitialURL and subscribe are the right hooks. Another classic: canOpenURL always returning false on iOS because the queried scheme is not listed in LSApplicationQueriesSchemes, and on Android 11 and later because the <queries> element is missing from the manifest.
To test: npx uri-scheme open myapp://order/42 --ios, or xcrun simctl openurl booted and adb shell am start -W -a android.intent.action.VIEW -d. I ask for a test matrix: custom link and https link, app closed, in the background, and in the foreground, on both platforms.
Related terms
Universal links and App Links
Ordinary https links that open the app when installed, proven by a file hosted on your domain (AASA, assetlinks.json).
Expo Router
Expo’s file-based router: every file in the app directory becomes a screen, with deep links and typed routes out of the box.
React Navigation
The standard navigation library for React Native: stack, tabs, drawer, with native screens through react-native-screens.
OTA update (EAS Update)
Shipping a new JavaScript bundle and its assets to installed apps without going through the stores; EAS Update is Expo’s service for it.