How to Handle Deep Linking in a React Native App
Note: This tutorial was published in 2022 and uses React Navigation v6.
While the core concepts of deep linking and navigation remain the same, always refer to the official React Navigation documentation for the latest API usage and configuration best practices.
Deep linking allows a URL to open a specific screen within a mobile application, rather than just launching the app to its home screen. This technique is essential for creating seamless user experiences, particularly for marketing campaigns, notifications, and web-to-app user flows.
This tutorial demonstrates how to implement and handle deep links in a React Native application using the React Navigation library (v6). We will build a simple two-screen application to showcase the configuration process for both iOS and Android.
The source code for the example app is available at this GitHub Repo.
Setting up Navigation
First, create a new React Native application.
npx react-native init rnDeepLinking
cd rnDeepLinking
Next, install the required dependencies for React Navigation v6.
yarn add @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context
For iOS, you must install the pods.
npx pod-install ios
For Android, additional configuration is required in android/app/src/main/java/com/rndeeplinking/MainActivity.java. Add the following onCreate method:
package com.rndeeplinking;
import android.os.Bundle;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
}
Creating the App Screens
Create a src/screens directory with two files: HomeScreen.js and DetailsScreen.js.
The HomeScreen.js will fetch and display a list of users from the JSON Placeholder API. Each item will be pressable to navigate to the details screen.
// src/screens/HomeScreen.js
import React, { useState, useEffect } from 'react';
import { ActivityIndicator, View, Text, FlatList, Pressable } from 'react-native';
import Separator from '../components/Separator';
const HomeScreen = ({ navigation }) => {
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(res => {
setData(res);
setIsLoading(false);
});
}, []);
const renderList = ({ item }) => (
<Pressable
onPress={() => navigation.navigate('Details', { personDetailsId: item.id })}
style={{ paddingHorizontal: 10 }}
>
<Text style={{ fontSize: 24, color: '#000' }}>{item.name}</Text>
</Pressable>
);
return (
<View style={{ flex: 1 }}>
{isLoading ? (
<ActivityIndicator color="blue" size="large" />
) : (
<FlatList
data={data}
keyExtractor={item => item.id}
ItemSeparatorComponent={Separator}
renderItem={renderList}
/>
)}
</View>
);
};
export default HomeScreen;
Create a simple Separator component at src/components/Separator.js for the list.
The DetailsScreen.js will initially just display placeholder text.
// src/screens/DetailsScreen.js
import React from 'react';
import { View, Text } from 'react-native';
const DetailsScreen = () => {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details Screen</Text>
</View>
);
};
export default DetailsScreen;
Configuring the Stack Navigator
Create a src/navigation/RootNavigator.js file to define the stack navigator.
// src/navigation/RootNavigator.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from '../screens/HomeScreen';
import DetailsScreen from '../screens/DetailsScreen';
const RootStack = createNativeStackNavigator();
const RootNavigator = () => (
<NavigationContainer>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={HomeScreen} />
<RootStack.Screen name="Details" component={DetailsScreen} />
</RootStack.Navigator>
</NavigationContainer>
);
export default RootNavigator;
Render this RootNavigator in App.js. After building the app, you should have a functioning two-screen application.
Configuring Deep Linking
To enable deep linking, you need to provide a linking configuration object to the NavigationContainer. This object defines the URI schemes your app will respond to.
In RootNavigator.js, define the linking object:
import { ActivityIndicator } from 'react-native';
const linking = {
prefixes: ['peoplesapp://'],
config: {
initialRouteName: 'Home',
screens: {
Home: {
path: 'home'
},
Details: {
path: 'details/:personId' // Add a dynamic parameter
}
}
}
};
const RootNavigator = () => (
<NavigationContainer
linking={linking}
fallback={<ActivityIndicator color="blue" size="large" />}
>
{/* ... Navigator ... */}
</NavigationContainer>
);
Adding the URI Scheme to Native Projects
The URI scheme must be registered in the native iOS and Android projects.
Using uri-scheme Package
The easiest way to configure the schemes is with the uri-scheme package.
# For iOS
npx uri-scheme add peoplesapp --ios
# For Android
npx uri-scheme add peoplesapp --android
Rebuild your app after running these commands.
Manual iOS Configuration
To manually configure iOS, first add the RCTLinkingManager to AppDelegate.m. Then, in Xcode, navigate to your project's Info tab and add a URL Type, setting both the Identifier and URL Schemes to peoplesapp.
Manual Android Configuration
For Android, open android/app/src/main/AndroidManifest.xml. Set launchMode="singleTask" on the <activity> tag and add a new <intent-filter> to handle your custom scheme.
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
...>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="peoplesapp" />
</intent-filter>
</activity>
Rebuild your app after making these changes.
Testing Deep Links
You can test your deep links from the command line.
For iOS:
xcrun simctl openurl booted peoplesapp://details/1
For Android:
adb shell am start -W -a android.intent.action.VIEW -d "peoplesapp://details/1"
These commands should open your app directly to the Details screen.
Handling Dynamic Parameters
The deep link path details/:personId includes a dynamic parameter. This parameter is accessible in the screen component via route.params.
Modify DetailsScreen.js to fetch data based on the ID passed either through navigation params (personDetailsId) or deep link params (personId).
// src/screens/DetailsScreen.js
import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
const DetailsScreen = ({ route }) => {
const params = route.params || {};
const { personDetailsId, personId } = params;
const idToFetch = personId || personDetailsId;
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (idToFetch) {
fetch(`https://jsonplaceholder.typicode.com/users/${idToFetch}`)
.then(res => res.json())
.then(res => {
// Format data for display
const fetchedDetails = Object.keys(res).map(key => ({ key, value: `${res[key]}` }));
setData(fetchedDetails);
})
.finally(() => setIsLoading(false));
}
}, [idToFetch]);
return (
<View style={{ flex: 1, paddingTop: 10, paddingHorizontal: 10 }}>
{isLoading ? (
<ActivityIndicator color="blue" size="large" />
) : (
data.map(person => (
<Text key={person.key}>{`${person.key}: ${person.value}`}</Text>
))
)}
</View>
);
};
export default DetailsScreen;
Now, whether you navigate from the home screen or open a deep link, the DetailsScreen will fetch and display the correct user's information.
Conclusion
This tutorial provided a comprehensive walkthrough of configuring deep linking in a React Native application using React Navigation. You learned how to define URL schemes, configure the navigation container, and handle both static and dynamic route parameters.
Implementing deep linking can significantly improve your application's user experience and is a key feature for user engagement and retention.