Implementing Deep Linking and Universal Links in React Native: A Complete Guide
What is Deep Linking?
Deep linking enables your app to respond to external URLs and custom URI schemes, allowing users to:
Open your app from web links
Navigate directly to specific screens or content
Share direct links to app content
Handle marketing campaign links
Deep linking in React Native comes in two flavours:
Custom URL Schemes (e.g.,
myapp://
)Universal Links (iOS) and App Links (Android) using HTTPS URLs
While custom URL schemes are easier to implement, Universal/App Links provide better security and user experience since they use verified HTTPS URLs.
Prerequisites
Before we begin, ensure you have:
A React Native project set up
React Navigation installed (
@react-navigation/native
)Basic understanding of React Navigation
Implementation Steps
1. Configure Your App to Handle Deep Links
First, you'll need to modify your Android and iOS configurations to recognise your deep links.
For Android (in android/app/src/main/AndroidManifest.xml
):
xmlCopy<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="myapp" />
</intent-filter>
</activity>
For iOS (in ios/YourApp/Info.plist
):
xmlCopy<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
2. Universal Links Setup
First we will start with config concerned to server side. Here I will be taking a node.js server for explaining the configs.
Create a public folder in root directory of your project, in that folder create another folder named ".well-known”.
Make sure you add below line of code in your App.js/index.js
file of your serverapp.use(express.static(path.join(__dirname, "public")));
Server Code GitHub Link
For IOS:
In ".well-known” directory create a file named apple-app-site-association
(no extension):
{
"applinks": {
"apps": [],
"details": [
{
"appID": "TEAM_ID.com.yourapp.bundleid",
"paths": ["*"]
}
]
}
}
Note: You can find the Team_ID on apple developer account website in Membership details section. The string after Team_ID is your bundle ID, you can find in general section by opening your project in Xcode.
Update Info.plist
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:yourdomain.com</string>
</array>
For Android:
In ".well-known” directory create a file named assetlinks.json
:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.yourapp.package",
"sha256_cert_fingerprints": ["YOUR-SHA256-CERTIFICATE"]
}
}]
Replace the "com.yourapp.package" with value available at “namespace” key in your app level build.gradle file.
The sha256_cert_fingerprints can be generated by runnning ./gradlew signingReport
command in android directory of your project. It will generate two-three SHA-keys. You need to add only distinctive keys here.
Update AndroidManifest.xml
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:usesCleartextTraffic="true"
android:exported="true">
<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="https"
android:host="yourdomain.com"
android:pathPrefix="/" />
</intent-filter>
</activity>
Verify that below written snippets are present in AndroidManifest.xml file.android:usesCleartextTraffic="true" android:launchMode="singleTask android:autoVerify="true"
3. Configure Navigation
const linking = {
prefixes: [
'myapp://', // Basic deep linking
'https://yourdomain.com' // Universal/App Links
],
config: {
screens: {
Home: 'home',
Profile: {
path: 'user/:id',
parse: {
id: (id) => `${id}`,
},
},
Settings: 'settings',
},
},
};
// In your Navigation Container
function App() {
return (
<NavigationContainer linking={linking}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
In our ProfileScreen
component, we can then access the id
parameter from the route.params
object:
import { useRoute } from '@react-navigation/native';
function ProfileScreen() {
const route = useRoute();
const { id } = route.params;
return (
<View>
<Text>User Profile: {id}</Text>
{/* Render profile content */}
</View>
);
}
Most important thing to do after All the Configs:
Delete the app from your emulator/ simulator/device, reinstall the pods(for IOS) and build your app with reset-cache command.
Debugging Universal Links:
# Verify AASA file
curl -v https://yourdomain.com/.well-known/apple-app-site-association
# Verify Asset Links
curl -v https://yourdomain.com/.well-known/assetlinks.json
Testing Using uri-scheme
For testing deep links on the iOS simulator, you can use the uri-scheme
package. This tool allows you to easily open deep links in your app during development and testing.
To install the uri-scheme
package, run the following command:
npm install -g uri-scheme
Once installed, you can open deep links in the iOS simulator using the following command:
npx uri-scheme open "YOUR_DEEP_LINK_URL" --ios
Replace "YOUR_DEEP_LINK_URL"
with the actual deep link you want to test. For example:
npx uri-scheme open "myapp://profile/123" --ios
npx uri-scheme open "https://myapp.com/profile/456" --ios
npx uri-scheme open "myapp://profile/123" --android
npx uri-scheme open "https://myapp.com/profile/456" --android
Subscribe to my newsletter
Read articles from Sahil Mohammad directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by