Configuring Deep Linking in React Navigation v6 to Sync App State with URLs
Learn how to configure React Navigation v6’s linking prop to parse incoming URLs and automatically navigate to the correct screen, with a practical Expo example and platform‑specific checks.
01 Dec 2025, 09:06 UTC

Problem: Incoming URLs don’t open the right screen
You have a React Native app built with React Navigation v6. When a user taps a link like myapp://product/42 the app launches, but it stays on the home screen instead of navigating to the Product detail screen with productId=42. This breaks the expected deep‑link behavior and forces users to navigate manually.
Thesis
By supplying a linking configuration object to NavigationContainer you can teach React Navigation how to parse incoming URLs and automatically dispatch the corresponding navigation actions. The configuration works uniformly on Android, iOS, and (with extra setup) the web, keeping the navigation state in sync with the URL.
Understanding the linking prop
The linking prop accepts an object that defines:
prefixes– the URL schemes or host patterns your app should recognize.config– a map of screen names to path strings, where:paramIdsyntax extracts route parameters.- Optional
getStateFromURLandgetURLFromStatefunctions for custom serialization (e.g., handling query params or hash fragments).
When a link matches a prefix, React Navigation runs getStateFromURL to produce a navigation state object, which it then merges with the navigator’s current state. The reverse function getURLFromState updates the browser URL on the web or updates the Linking API on mobile when the navigation state changes.
Worked example: Setting up deep links in an Expo project
- Install dependencies (run in your project root):
npm install @react-navigation/native @react-navigation/native-stack # For web support (optional) npm install @react-navigation/web # Expo linking is needed for standalone builds expo install expo-linkingRun this in a terminal with access to the project directory. No special permissions are required beyond normal npm/expo access.
- Configure app.json (Expo managed workflow): add the scheme under
expo.scheme:{ "expo": { "name": "MyApp", "scheme": "myapp", "plugins": [] } }After changing
app.json, rebuild the development client or runexpo prebuildto regenerate native configs. - Create the linking configuration in your entry file (e.g.,
App.tsx):import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); const linking = { prefixes: ['myapp://'], config: { screens: { Root: 'home', Product: 'product/:productId', Profile: 'profile/:userId', }, }, }; export default function App() { return ( Loading…}> ); }Each screen’s path defines how URL segments map to route parameters. For example,
product/:productIdwill extractproductIdfrommyapp://product/42and pass it as a prop toProductScreen. - Test the deep link:
- Android:
adb shell am start -W -a android.intent.action.VIEW -d \"myapp://product/42\" - iOS simulator:
xcrun simctl openurl booted myapp://product/42 - Web (if you added
@react-navigation/web): serve the app locally (npm startorexpo start --web) and navigate tohttp://localhost:3000/product/42.
Expected check: the navigator should display the Product screen and you can verify the route parameter by logging
route.params.productIdinsideProductScreen. No full reload should occur on web when using the browser’s back/forward buttons. - Android:
Trade‑offs and limitations
- Version differences – In React Navigation v5 the
linkingprop lived on individual navigators and used a different path syntax. Upgrading to v6 requires moving the config toNavigationContainerand adjusting any:paramplaceholders. - Web setup – Without
@react-navigation/weband a bundler that resolves platform‑specific modules (e.g., Metro or webpack withaliasforreact-native), thelinkingprop will cause a runtime error because the native linking modules are missing. - Query parameters and hash fragments – The default
getStateFromURLonly processes the pathname. To handle?filter=activeor#sectionyou must provide custom functions that merge those values into the navigation state. - Expo managed workflow – Forgetting to add the scheme to
app.jsonor to runexpo prebuildafter changing it results in deep links being ignored in standalone builds, even though they work in the Expo Go client.
Actionable closing
Start by adding a minimal linking object with your app’s scheme and the screens you want to expose via URLs. Verify the behavior on each target platform using the adb/simctl commands or a local web server. If you need query‑string support, replace the default parsers with custom getStateFromURL and getURLFromState implementations. Once the configuration is in place, incoming URLs will reliably drive your navigation state, giving users a seamless deep‑link experience across Android, iOS, and the web.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.