React Navigation Deep Linking: Platform‑Specific Prefix Configuration
Configuring iOS and Android deep‑link prefixes, TypeScript param types, and focus‑event handling so routes inject reliably across platforms.
20 Dec 2025, 02:58 UTC

Deep linking should bridge your React Native app to the web, but when the React Navigation linking config misses an ios or android prefix, the link opens on one platform and silently fails on the other. This post distills the practical setup so you can inject routes reliably regardless of OS.
The linking config anatomy
React Navigation expects a linking object passed to any navigator. The object maps URL patterns to screen keys and automatically parses query parameters into route data. A minimal config that works across platforms looks like this:
const linking = {
ios: {
config: {
schemes: ['myapp'],
},
},
android: {
package: 'com.myapp',
scheme: 'myapp',
},
default: 'home',
};
iOS uses config.schemes (an array), while Android uses package and scheme. Omitting either block causes deep links to fail silently on that operating system.
Type‑safe param lists with LinkingParamList
When screens accept query parameters, TypeScript can enforce their shapes. Declare a LinkingParamList type that mirrors the params each screen expects:
type LinkingParamList = {
home: undefined;
profile: { userId: string };
};
Pass this type to the navigator:
const HomeStack = createStackNavigator();
Now navigation.navigate('profile', { userId: '123' }) and navigation.getState().routes[0].params are typed, and deep‑link parsed params obey the same contract.
Reacting to deep links with useFocusEffect
When a deep link brings the user back to a screen, useFocusEffect fires and can reload data based on the parsed params:
import { useFocusEffect } from '@react-navigation/native';
import { useRoute } from 'react-navigation';
const ProfileScreen = () => {
const route = useRoute();
const { userId } = route.params;
useFocusEffect(() => {
// Reload data when the screen gains focus via a deep link
if (userId) fetchProfile(userId);
return () => {};
});
return ;
};
Diagnostic check and practical verification
Android
In a terminal with ADB access, device or emulator running, launch the app with a test intent that matches your prefix:
adb shell am start -W -d myapp://profile?id=5
Where to run: A terminal with the Android SDK/platform‑tools; device or emulator with your app installed.
Required permissions: ADB is part of the Android SDK; no special permissions beyond running Android Studio are needed.
Meaningful placeholders: Replace myapp with your actual scheme; replace profile?id=5 with your route pattern and param values.
Expected check: After the intent completes, open the app and inspect the route. React Navigation should have parsed { userId: '5' } into the route. Verify by running the intent and checking the console output or using adb shell logcat to surface the navigation state.
Relevant risks: If another app registered the same scheme, the intent may launch that app instead. Disambiguate prefixes or use Android App Links (android:autoVerify) to reclaim the scheme.
iOS (simulator)
In Xcode’s simulator, open Safari or another app and navigate to myapp://profile?id=5. The app should launch, and React Navigation’s linking handler logs the payload.
Where to run: iOS simulator with the app built and running.
Required permissions: Developer mode on the Mac; no extra permissions beyond Xcode.
Meaningful placeholders: Replace myapp with your URL scheme.
Expected check: Check the Xcode console for a log line from React Navigation’s deep‑link handler, typically something like Deep link received: { userId: '5' }. Alternatively, add console.log('deep link', linking.getInitialURL()) in a useEffect at the root to see the parsed URL.
Relevant risks: If the scheme is ambiguous, iOS may prompt “Open in “Your App” or “Cancel”. Test with a custom scheme unlikely to conflict with system services.
Limitation: Conflicting URL schemes
Even with correct ios and android blocks, other apps or system services may register the same URL scheme. This can cause links to open in the wrong app or be intercepted before React Navigation sees them.
Practical way to check: On Android, run adb shell dumpsys package your.package.name | grep -i intent to see intent filters. On iOS, use defaults read com.apple.LaunchServices/com.apple.launchservices.secure to inspect registered schemes. If conflicts appear, adopt a more specific scheme (e.g., include a reverse‑domain prefix like com.mycompany.app) or implement a fallback deep‑link handler that asks the user to confirm.
Actionable closing
- Add both
iosandandroidblocks to yourlinkingobject, matching your app’s package and scheme. - Declare a
LinkingParamListTypeScript type that maps screen names to param shapes. - Use
useFocusEffecton screens that receive deep‑link params to reload or sync data. - Verify with the Android
adbintent or iOS simulator scheme test described above. - Check for scheme conflicts and, if needed, adopt a reverse‑domain‑prefixed scheme.
Following this checklist gives you reliable web‑to‑app navigation across iOS and Android, with typed params and automatic data sync when the user returns to a screen.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.