Deep linking is a powerful feature in mobile apps that allows users to navigate directly to specific screens or content using a URL. In React Native, deep linking can greatly enhance user experience — especially when integrated with navigation libraries and Firebase Dynamic Links. Whether users open your app from an email, a website, or a shared link, deep linking ensures they land exactly where they need to be.
In this tutorial, we’ll walk you through how to implement deep linking in a React Native app using React Navigation and Firebase Dynamic Links. You’ll learn how to configure URL schemes, handle incoming links, and test deep links across Android and iOS. By the end, you’ll have a fully working example that supports both app-to-app and web-to-app navigation.
What You’ll Learn
In this tutorial, you’ll learn how to:
-
Configure deep linking in a React Native project.
-
Integrate React Navigation to handle in-app routes.
-
Set up Firebase Dynamic Links for smart cross-platform URLs.
-
Test and verify deep links on both Android and iOS devices.
-
Handle link parameters to display specific content or screens dynamically.
By the end of this guide, you’ll have a React Native app capable of opening precise screens directly from links — whether launched from a browser, another app, or a shared message.
Project Setup
Before diving into deep linking, let’s start by setting up a new React Native project and installing the essential dependencies.
Step 1: Create a New React Native Project
We’ll use the React Native Community CLI to create a fresh project. Open your terminal and run:
npx @react-native-community/cli init RNDeepLinkDemo
cd RNDeepLinkDemo
This will create a new React Native app named RNDeepLinkDemo.
Step 2: Install Required Dependencies
We’ll use React Navigation to manage screens and navigation within the app.
Install React Navigation and its core dependencies:
npm install @react-navigation/native
npm install react-native-screens react-native-safe-area-context
Next, install the stack navigator:
npm install @react-navigation/native-stack
Finally, install TypeScript type definitions:
npm install --save-dev @types/react @types/react-native
Step 3: Set Up React Navigation
Open the App.tsx file and replace its content with the following basic navigation setup:
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button } from 'react-native';
type RootStackParamList = {
Home: undefined;
Details: { id?: number };
};
const Stack = createNativeStackNavigator<RootStackParamList>();
function HomeScreen({ navigation }: { navigation: any }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 20 }}>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { id: 42 })}
/>
</View>
);
}
function DetailsScreen({ route }: { route: any }) {
const { id } = route.params || {};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 20 }}>Details Screen</Text>
<Text>ID: {id}</Text>
</View>
);
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Now, run the app to ensure everything works properly:
npm run android
# or
npm run ios
You should see a simple app with two screens — Home and Details — navigable via a button press.

Configure Deep Linking in React Navigation (TypeScript)
React Navigation provides built-in support for deep linking, allowing your app to open specific screens when a user taps a link. In this section, we’ll configure deep linking and test it on both Android and iOS.
Step 1: Define a Deep Link Configuration
In your project root, open App.tsx and update it with a linking configuration. This configuration maps URL paths to navigation routes.
Replace your App.tsx file with the following:
import * as React from 'react';
import { NavigationContainer, LinkingOptions } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button } from 'react-native';
type RootStackParamList = {
Home: undefined;
Details: { id?: number };
};
const Stack = createNativeStackNavigator<RootStackParamList>();
const linking: LinkingOptions<RootStackParamList> = {
prefixes: ['rnlinkdemo://', 'https://rnlinkdemo.page.link'],
config: {
screens: {
Home: 'home',
Details: 'details/:id?',
},
},
};
function HomeScreen({ navigation }: { navigation: any }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 20 }}>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { id: 42 })}
/>
</View>
);
}
function DetailsScreen({ route }: { route: any }) {
const { id } = route.params || {};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 20 }}>Details Screen</Text>
<Text>ID: {id}</Text>
</View>
);
}
export default function App() {
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
In this example:
-
prefixesdefines the URL schemes that trigger your app (rnlinkdemo://for a custom scheme, andhttps://rnlinkdemo.page.linkfor Firebase Dynamic Links). -
configmaps paths (e.g.,/details/:id) to screens in your navigation stack.
Step 2: Configure Android Deep Links
Open the file android/app/src/main/AndroidManifest.xml and add an intent filter inside the <activity> tag:
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="rnlinkdemo" android:host="details" />
</intent-filter>
This tells Android to open the app when a link like rnlinkdemo://details/42 is clicked.
Step 3: Configure iOS URL Schemes
-
In Xcode, open your project.
-
Select your app target → Info tab.
-
Expand URL Types, click +, and set:
-
Identifier:
rnlinkdemo -
URL Schemes:
rnlinkdemo
-
Now iOS will recognize URLs starting with rnlinkdemo://.
Step 4: Test Your Deep Links
Run your app on a simulator or device, then test the links:
Android (via terminal):
adb shell am start -W -a android.intent.action.VIEW -d "rnlinkdemo://details/99" com.rnlinkdemo
iOS (via terminal):
xcrun simctl openurl booted rnlinkdemo://details/99
You should see your app open directly on the Details screen, showing ID: 99.
✅ At this stage, your app supports deep linking with custom URL schemes and navigation routing.
Set Up Firebase Dynamic Links
Now that your app supports basic deep linking with custom URL schemes, let’s take it further using Firebase Dynamic Links — a smarter, more flexible approach that works across platforms (iOS, Android, and web). Dynamic Links survive the app installation process and route users directly to the intended screen once the app is opened.
Step 1: Create a Firebase Project
-
Go to Firebase Console.
-
Click Add project and create a new one (e.g.,
RNDeepLinkDemo). -
Once created, open your project dashboard.
Step 2: Enable Dynamic Links
-
In the left sidebar, select Engage → Dynamic Links.
-
Click Get Started.
-
Set up a domain for your links — for example:
https://rnlinkdemo.page.link -
Copy this domain; we’ll use it in both Android and iOS configurations and in your React Navigation
prefixes.
Step 3: Add Android App to Firebase
-
Go to your Firebase project overview.
-
Click the Android icon to add an app.
-
Enter your app’s package name (check in
android/app/build.gradle, underapplicationId, e.g.com.rnlinkdemo). -
Download the
google-services.jsonfile and move it to your project’sandroid/app/folder. -
Add the Firebase Gradle dependencies:
Edit android/build.gradle:
buildscript {
dependencies {
classpath("com.google.gms:google-services:4.4.2")
}
}
Edit android/app/build.gradle:
apply plugin: 'com.google.gms.google-services'
Step 4: Add iOS App to Firebase (Optional but Recommended)
-
In Firebase, click the iOS icon to add an app.
-
Enter your iOS bundle ID (found in Xcode → Target → General → Identity).
-
Download the
GoogleService-Info.plistfile and add it to your iOS project (Xcode → drag into Runner root). -
Add the
Firebase/Corepod:
cd ios
pod install
cd ..
Step 5: Install Firebase Dynamic Links Package
To handle Dynamic Links inside React Native, install the React Native Firebase Dynamic Links package:
npm install @react-native-firebase/app @react-native-firebase/dynamic-links
Then rebuild your project:
npm run android
# or
npm run ios
Step 6: Handle Dynamic Links in Your App
Open App.tsx and add Firebase Dynamic Link handlers.
This ensures that when the app is opened via a Dynamic Link, the link is parsed and navigation occurs automatically.
Update your App.tsx like so:
import * as React from 'react';
import { useEffect } from 'react';
import { NavigationContainer, LinkingOptions } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button } from 'react-native';
import dynamicLinks from '@react-native-firebase/dynamic-links';
type RootStackParamList = {
Home: undefined;
Details: { id?: number };
};
const Stack = createNativeStackNavigator<RootStackParamList>();
const linking: LinkingOptions<RootStackParamList> = {
prefixes: ['rnlinkdemo://', 'https://rnlinkdemo.page.link'],
config: {
screens: {
Home: 'home',
Details: 'details/:id?',
},
},
};
function HomeScreen({ navigation }: { navigation: any }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 20 }}>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { id: 42 })}
/>
</View>
);
}
function DetailsScreen({ route }: { route: any }) {
const { id } = route.params || {};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 20 }}>Details Screen</Text>
<Text>ID: {id}</Text>
</View>
);
}
export default function App() {
const handleDynamicLink = (link: any) => {
if (link?.url) {
const match = link.url.match(/details\/(\d+)/);
if (match) {
// Navigate to details screen
navigationRef.current?.navigate('Details', { id: Number(match[1]) });
}
}
};
useEffect(() => {
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
dynamicLinks()
.getInitialLink()
.then(handleDynamicLink);
return () => unsubscribe();
}, []);
const navigationRef = React.useRef<any>(null);
return (
<NavigationContainer ref={navigationRef} linking={linking}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Now your app listens for Firebase Dynamic Links — even if the app is closed before opening the link.
Step 7: Test a Firebase Dynamic Link
In Firebase Console → Dynamic Links, click New Dynamic Link, and create one:
-
Domain:
https://rnlinkdemo.page.link -
Deep link URL:
https://rnlinkdemo.page.link/details/101 -
Android package name: your app package (e.g.
com.rnlinkdemo) -
iOS bundle ID: your iOS bundle
After creation, open the generated short link on your device — it should open your app and navigate to the Details screen showing ID: 101.
✅ At this point, your app fully supports:
-
Custom scheme deep linking (
rnlinkdemo://details/42) -
Firebase Dynamic Links (
https://rnlinkdemo.page.link/details/42) -
Automatic routing to specific screens
Testing Deep Links on Devices and Emulators
Now that you’ve implemented both React Navigation deep linking and Firebase Dynamic Links, it’s time to test your setup across different environments. Testing deep links ensures users are directed to the correct screen no matter how the link is opened — from a browser, email, or social media.
Step 1: Test Custom Deep Links (App URL Scheme)
🟢 Android
You can test custom scheme links using ADB (Android Debug Bridge).
Make sure your emulator or device is running, then execute:
adb shell am start -W -a android.intent.action.VIEW -d "rnlinkdemo://details/42" com.rnlinkdemo
This should open your app and navigate directly to the Details screen, showing ID: 42.
💡 Tip: Replace
com.rnlinkdemowith your actual package name if different.
🟣 iOS
Use the Xcode command line tools to simulate a link click:
xcrun simctl openurl booted rnlinkdemo://details/42
Your app should launch (or come to the foreground) and open the Details screen.
Step 2: Test Firebase Dynamic Links
🧩 Generate a Dynamic Link
If you haven’t already, go to Firebase Console → Engage → Dynamic Links and create a new one.
Example:
https://rnlinkdemo.page.link/details/99
Make sure:
-
The deep link URL includes a valid path (
/details/99). -
The Android package name matches your app.
-
The iOS bundle ID is correctly set.
🔗 Android Testing
-
Copy the generated Dynamic Link.
-
Paste it into Chrome or send it to your device via email/message.
-
Tap the link — if the app is installed, it should open directly to the Details screen with
ID: 99.
If not installed, it will first redirect to the Play Store.
You can also test using ADB:
adb shell am start -W -a android.intent.action.VIEW -d "https://rnlinkdemo.page.link/details/99" com.rnlinkdemo
🍎 iOS Testing
-
Copy and paste the same Firebase Dynamic Link into Safari on your simulator or physical device.
-
Tap “Open” when prompted.
-
The app should launch and route directly to the Details screen.
Step 3: Test App in Background and Closed States
Deep links should work whether your app is running, in the background, or completely closed. Test all three states:
-
Foreground: Tap the link while the app is open.
-
Background: Minimize the app and tap the link.
-
Closed: Fully close the app, then open it via a link.
✅ Each case should result in the same behavior — navigation to the Details screen with the correct ID parameter.
Step 4: Debugging Tips
If your deep links don’t open correctly:
-
Double-check the prefixes in your
linkingconfig. -
Verify your intent filter in
AndroidManifest.xml. -
Confirm your Firebase Dynamic Link domain matches what’s configured in your
prefixes. -
Add a console log inside
handleDynamicLinkinApp.tsxto trace incoming URLs:
const handleDynamicLink = (link: any) => {
console.log('Received link:', link?.url);
// Handle navigation...
};
Step 5: Bonus — Shareable Links for Testing
You can generate testable links dynamically from your Firebase Console or by using the REST API:
Example generated link:
https://rnlinkdemo.page.link/?link=https://rnlinkdemo.page.link/details/101&apn=com.rnlinkdemo
Share this link via email or message — clicking it should open your app to Details with ID: 101.
✅ At this point, you’ve successfully tested:
-
Custom URL scheme deep links
-
Firebase Dynamic Links
-
Behavior across foreground, background, and closed states
Handling Link Parameters and Navigation Logic
Now that your app can open from a deep link, the next step is to extract and handle parameters from the incoming URLs.
This is useful when your links contain identifiers, like product IDs, article slugs, or user IDs.
For example, a link such as:
rnlinkdemo://details/42
should navigate to the Details screen and display the ID 42.
Step 1: Update the Linking Configuration
Your linking configuration already defines a dynamic segment for Details. Let’s make sure it’s typed and mapped correctly.
In App.tsx, verify your linking setup includes the correct path and parameter:
const linking: LinkingOptions<RootStackParamList> = {
prefixes: ['rnlinkdemo://', 'https://rnlinkdemo.page.link'],
config: {
screens: {
Home: 'home',
Details: 'details/:id?',
},
},
};
This ensures that any URL matching /details/:id (e.g., /details/101) is passed to the Details route as a parameter.
Step 2: Extract Parameters from React Navigation
Inside your DetailsScreen, React Navigation automatically provides parameters passed via the route — including those parsed from deep links.
Update the DetailsScreen as follows:
import { RouteProp } from '@react-navigation/native';
type DetailsScreenRouteProp = RouteProp<RootStackParamList, 'Details'>;
function DetailsScreen({ route }: { route: DetailsScreenRouteProp }) {
const { id } = route.params || {};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 20 }}>Details Screen</Text>
<Text>ID: {id ? id : 'No ID provided'}</Text>
</View>
);
}
This approach uses TypeScript types for better reliability and autocompletion support.
Step 3: Parse Parameters from Firebase Dynamic Links
Firebase Dynamic Links often use full URLs (e.g., https://rnlinkdemo.page.link/details/42).
When handling them, we can use regular expressions or URL parsing to extract parameters manually.
Update your handleDynamicLink function in App.tsx:
const handleDynamicLink = (link: any) => {
if (link?.url) {
console.log('Received link:', link.url);
try {
// Use the WHATWG URL API if available
let url: any;
if (typeof URL !== 'undefined') {
url = new URL(link.url);
} else {
// Fallback for React Native environments
const match = link.url.match(/:\/\/([^/]+)\/([^/]+)\/?(\d+)?/);
if (match) {
const [, host, screen, id] = match;
if (screen === 'details' && id) {
navigationRef.current?.navigate('Details', { id: Number(id) });
}
}
return;
}
// If URL parsing works normally
const pathSegments = url.pathname?.split('/') ?? [];
const screen = pathSegments[1];
const id = pathSegments[2];
if (screen === 'details' && id) {
navigationRef.current?.navigate('Details', { id: Number(id) });
}
} catch (error) {
console.warn('Error parsing dynamic link:', error);
}
}
};
This ensures that regardless of how the link is formatted, your app correctly interprets it and navigates to the right screen.
Step 4: Add a Loading or Fallback Screen (Optional)
Sometimes a user may open a deep link before your app finishes initializing (especially on cold starts). To handle this gracefully, you can add a fallback or splash screen while waiting for the navigation container to mount.
Example:
<NavigationContainer
ref={navigationRef}
linking={linking}
fallback={<Text>Loading...</Text>}
>
This displays a brief “Loading…” message until navigation is ready.
Step 5: Test with Various Parameter Scenarios
Try opening your app with different link variations to ensure robust handling:
| Link | Expected Behavior |
|---|---|
rnlinkdemo://details/1 |
Navigates to Details screen with ID 1 |
https://rnlinkdemo.page.link/details/2 |
Opens via Firebase Dynamic Link → navigates to ID 2 |
rnlinkdemo://details |
Opens Details screen with “No ID provided” |
rnlinkdemo://home |
Opens Home screen |
Step 6: Add Defensive Checks
To prevent crashes from malformed URLs, always validate parsed data:
if (screen === 'details' && !isNaN(Number(id))) {
navigationRef.current?.navigate('Details', { id: Number(id) });
} else {
console.warn('Invalid deep link:', link.url);
}
✅ At this stage, your app can:
-
Parse and use URL parameters for dynamic content
-
Safely handle malformed or missing data
-
Work seamlessly with both React Navigation deep links and Firebase Dynamic Links
Conclusion and Next Steps
In this tutorial, you learned how to implement deep linking in a React Native app using both React Navigation and Firebase Dynamic Links — enabling users to jump directly into specific screens or content from a shared URL.
You’ve successfully:
-
Created a React Native project with TypeScript and React Navigation
-
Configured custom URL schemes for Android and iOS
-
Integrated Firebase Dynamic Links for smart, cross-platform links
-
Parsed and handled link parameters to open the correct screen dynamically
-
Tested links on real devices, emulators, and various app states (foreground, background, closed)
With these capabilities, your app is now deep-link–ready, enhancing user experience and engagement by allowing seamless content access across platforms.
🚀 Next Steps
If you’d like to take this project further, here are a few enhancements to consider:
-
Add Route Parameters for More Screens
Extend deep linking to other routes like/profile/:usernameor/product/:slug. -
Implement Authentication Flow
Redirect users to a login or signup screen if they open a deep link requiring authentication. -
Custom Link Builders
Create a small utility to generate Firebase Dynamic Links programmatically for sharing. -
Analytics and Attribution
Use Firebase Analytics to track how users interact with your deep links and campaigns. -
Universal Links (iOS) and App Links (Android)
Configure your domain for full universal link support, so users can open your app directly from standard web URLs.
By mastering deep linking, you’ve unlocked one of the most powerful tools in modern mobile app development — connecting users, content, and platforms effortlessly. 🎉
You can get the full source code on our GitHub.
That's just the basics. If you need more deep learning about React Native, you can take the following cheap course:
- React Native - The Practical Guide [2025]
- The Complete React Native + Hooks Course
- The Best React Native Course 2025 (From Beginner To Expert)
- React Native: Mobile App Development (CLI) [2025]
- React - The Complete Guide 2025 (incl. Next.js, Redux)
- React Native Development Simplified [2025]
- React Native by Projects: From Basics to Pro [2025]
- Full Stack Ecommerce Mobile App: React Native & Node.js 2025
- Learning Management System Mobile App using React Native
- React Native: Advanced Concepts
Thanks!
