# React Native Deep Linking with React Navigation v6: Complete Tutorial > Master deep linking in React Native with React Navigation v6. Learn linking config, screen mapping, TypeScript examples, and testing strategies. - Canonical: https://redirectly.app/blog/react-native-deep-linking-react-navigation - Site: [Redirectly](https://redirectly.app) — deferred deep linking for Flutter & React Native - Published: 2024-03-01 - Author: Redirectly - Category: React Native · 8 min read Learn how to implement deep linking in React Native apps using React Navigation v6. Master linking configuration, screen mapping with TypeScript, handling deferred deep links, and testing strategies for both iOS and Android platforms. ## Understanding Deep Linking in React Native **What is Deep Linking in React Native?** Deep linking in React Native allows your app to handle URLs and route them to specific screens. When users click a link from email, SMS, push notifications, or web pages, React Navigation intercepts that URL and navigates to the appropriate screen with the relevant data. ### URL Schemes (Custom Protocol) Custom URL schemes like 'myapp://' let you handle app-specific links. `myapp://product/123` — Works when app is installed ### Universal Links & App Links Standard HTTPS URLs with fallback to web when app isn't installed. `https://myapp.com/product/123` — Seamless iOS & Android experience React Navigation v6 provides a unified API to handle both URL schemes and universal/app links, making it easy to implement deep linking across iOS and Android with a single codebase. ## React Navigation Linking Configuration React Navigation v6 uses a 'linking' prop on the NavigationContainer to define how URLs map to navigation state. ### Basic Linking Configuration ```typescript import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); const linking = { prefixes: ['myapp://', 'https://myapp.com', 'https://*.myapp.com'], config: { screens: { Home: '/', Product: 'product/:id', User: 'user/:userId', NotFound: '*', }, }, }; export default function RootNavigator() { return ( ); } ``` **Prefixes:** Define the URL schemes and domains your app responds to. Use wildcard subdomains for branch links or deep link services. ## Screen Mapping from URL Paths React Navigation maps URL patterns to screens and extracts parameters. The 'config.screens' object defines these mappings using route patterns. ### Advanced Configuration Example ```typescript interface RootStackParamList { Home: undefined; Product: { id: string }; Cart: { items?: string[] }; Profile: { userId: string }; Settings: { section?: string }; } const linking = { prefixes: ['myapp://', 'https://myapp.com'], config: { screens: { // Exact matches Home: '', // Dynamic parameters Product: 'product/:id', // Multiple parameters Cart: 'cart/:itemId', // Optional parameters Profile: 'user/:userId', Settings: { path: 'settings/:section?', parse: { section: (section: string) => section?.toLowerCase(), }, }, // Nested navigators Account: { screens: { Orders: 'account/orders', Wishlist: 'account/wishlist/:categoryId?', }, }, // Fallback for unmapped routes NotFound: '*', }, }, }; ``` ### URL Examples - `myapp://product/123` → Product screen with id='123' - `https://myapp.com/user/john` → Profile screen with userId='john' - `myapp://settings/account` → Settings with section='account' ### Accessing Parameters ```typescript type ProductScreenProps = NativeStackScreenProps< RootStackParamList, 'Product' >; function ProductScreen({ route, navigation }: ProductScreenProps) { const { id } = route.params; useEffect(() => { // Fetch product with id fetchProduct(id); }, [id]); return ( Product {id} ); } ``` ## Handling Deferred Deep Links with Redirectly Deferred deep links handle cases where users click a link before installing your app. When they install and open the app for the first time, it should navigate to the intended screen. Redirectly makes this seamless. ### Integrating Redirectly for Deferred Deep Links ```typescript import { NavigationContainer } from '@react-navigation/native'; import { useEffect } from 'react'; import Redirectly from '@redirectly/react-native'; const linking = { prefixes: ['myapp://', 'https://myapp.com'], config: { screens: { Home: '', Product: 'product/:id', User: 'user/:userId', }, }, // Custom async handler for deferred deep links async getInitialURL() { // 1. Check if app was opened from a deep link (cold start) const url = await Linking.getInitialURL(); if (url != null) { return url; } // 2. Check Redirectly for deferred deep link try { const redirectlyUrl = await Redirectly.getInitialURL(); return redirectlyUrl; } catch (error) { console.error('Error fetching from Redirectly:', error); } // 3. Default to home screen return undefined; }, }; function RootNavigator() { useEffect(() => { // Handle deep links when app is already running const unsubscribe = Linking.addEventListener('url', ({ url }) => { // Use React Navigation to navigate const route = url.replace(/.*?:/\//, ''); // Navigation logic here }); return unsubscribe.remove; }, []); return ( {/* Navigation stacks */} ); } ``` **Benefit:** Redirectly handles the deferred deep link attribution, so your app knows the original intent even if the user installed from an app store link. ### Learn More About Deferred Deep Linking [Read the Complete Deferred Deep Linking Guide](https://redirectly.app/react-native-deferred-deep-linking.md) ## TypeScript Implementation Using TypeScript ensures type safety throughout your deep linking implementation. Here's a complete, production-ready example. ```typescript import { NavigationProp, useNavigation } from '@react-navigation/native'; import { NativeStackScreenProps } from '@react-navigation/native-stack'; // Type-safe parameter definitions export type RootStackParamList = { Home: undefined; Product: { id: string; category?: string }; UserProfile: { userId: string; tab?: 'orders' | 'reviews' }; Search: { query: string }; NotFound: undefined; }; // Screen-specific props type export type ProductScreenProps = NativeStackScreenProps< RootStackParamList, 'Product' >; // Complete linking configuration with full type safety export const linking = { prefixes: ['myapp://', 'https://myapp.com', 'https://www.myapp.com'], config: { screens: { Home: { path: '', }, Product: { path: 'product/:id', parse: { id: (id: string) => id, category: (category?: string) => category, }, }, UserProfile: { path: 'user/:userId', parse: { userId: (userId: string) => userId, tab: (tab?: string) => tab as 'orders' | 'reviews' | undefined, }, }, Search: { path: 'search/:query', parse: { query: (query: string) => decodeURIComponent(query), }, }, NotFound: '*', }, }, }; // Type-safe hook for navigation export function useTypedNavigation() { return useNavigation>(); } // Example screen component function ProductScreen({ route }: ProductScreenProps) { const { id, category } = route.params; return ( Product: {id} {category && Category: {category}} ); } // Example programmatic navigation with type checking function navigateToProduct(navigation: any, id: string, category?: string) { navigation.navigate('Product', { id, category, }); } ``` ## Testing Deep Links on iOS and Android ### iOS Testing **Method 1: Using Simulator** ```bash # First, build and run your app in iOS simulator npx react-native run-ios # Then open a deep link in the simulator xcrun simctl openurl booted myapp://product/123 # Or with universal links xcrun simctl openurl booted https://myapp.com/product/123 ``` **Method 2: Testing Universal Links** Universal links require a valid apple-app-site-association file on your server. For testing: ```bash # Check if your AASA file is properly configured # Open in Safari on iOS device: https://yourdomain.com/apple-app-site-association # Validate with our AASA validator # Visit: https://redirectly.app/aasa-validator ``` ### Android Testing **Method 1: Using Android Emulator** ```bash # Build and run app on Android emulator npx react-native run-android # Open a deep link using adb adb shell am start -W -a android.intent.action.VIEW -d myapp://product/123 # Or with universal links adb shell am start -W -a android.intent.action.VIEW -d https://myapp.com/product/123 ``` **Method 2: Testing App Links** App Links require a valid assetlinks.json file on your server. ```bash # Validate your assetlinks.json with: # Visit: https://redirectly.app/assetlinks-validator # Check digital asset links on device adb shell am start -a android.intent.action.VIEW -d https://yourdomain.com/product/123 ``` ### Testing Checklist - Test cold start (app not running) - Test warm start (app already running) - Test with invalid/non-existent IDs - Test with special characters in parameters - Test both URL schemes and universal/app links - Test on real devices and emulators - Verify AASA/assetlinks files are accessible - Test deferred deep links with fresh install ## Related Resources & Tools - [AASA Validator](https://redirectly.app/aasa-validator.md) — Validate your apple-app-site-association configuration - [Assetlinks Validator](https://redirectly.app/assetlinks-validator.md) — Validate your assetlinks.json for Android App Links - [Deferred Deep Links](https://redirectly.app/react-native-deferred-deep-linking.md) — Complete guide to handling deferred deep links ## Master Deep Linking Today! Deep linking with React Navigation v6 is a powerful way to improve user engagement and create seamless app experiences. By following this guide and testing thoroughly on both platforms, you'll have a robust deep linking implementation that handles both standard and deferred deep links. ## Related - [Universal Links in React Native: Complete iOS Setup](https://redirectly.app/blog/react-native-universal-links-ios.md) — Deep dive into setting up Universal Links on iOS with AASA configuration and best practices. - [What is Deferred Deep Linking?](https://redirectly.app/blog/what-is-deferred-deep-linking.md) — Learn how deferred deep linking works and why it's essential for post-install user experiences. --- Full site index for AI agents: https://redirectly.app/llms.txt