Deep Linking in iOS React Native Apps 🔗
Deep linking can be a tricky topic, especially when working with React Native iOS apps. When I first started implementing it, I encountered several challenges, from understanding URL Schemes to configuring Universal Links. In this guide, I’ll walk you through the essential steps for setting up deep linking in your app, sharing what I’ve learned along the way so you can avoid the common pitfalls and get your deep linking setup right from the start. We’ll mainly cover:
- What is deep linking?
- The different deep linking methods (URL Schemes, Universal Links & Deferred Deep Linking)
- Implementing deep link in React Native iOS
- How to test?
So, let’s dive in and make deep linking work for you! 🚀

📌 What is deep linking?
Deep linking allows apps to handle URLs that open specific screens rather than just launching the app’s home screen.
For example:
- Clicking
testapp://profile/123
should open the Profile screen of user 123. - Clicking
https://yourwebsite.com/product/123
should open the Details screen for product 123.
Whether it’s opening a profile page, a product page, or a referral sign-up, deep linking creates a seamless user experience and increases engagement. However, implementing deep linking in React Native iOS apps involves multiple techniques (URL Schemes, Universal Links, and Deferred Deep Linking) with its own setup steps.
📌 The different deep-linking methods
There are three types of deep linking:
- URL Schemes — The traditional deep linking method using custom URI (
testapp://profile/123
) - Universal Links — A modern approach using https links (
https://yourwebsite.com/profile/123
), which works across iOS apps and browsers. - Deferred Deep Linking — Ensures users land on the correct screen even after they install the app via the link
Please read the table below(Figure A) for a detailed comparison between these 3 types of deep linking techniques.

📌 Implementing Deep Linking in React Native iOS
Step 1: Set Up URL Schemes
1. Configure URL Schemes in Xcode
- Open Xcode → Project Settings → Info tab.
- Scroll to URL Types → Click + to add a new URL scheme.
- Specify the identifier and the URL scheme using a preferred name, as in Figure B.
Identifier:com.testapp
URL Schemes:testapp

2. Handle incoming app links
To handle incoming app links to the app, add the following code to the AppDelegate.m
file in your codebase
// Add the below header at the top of the file
#import <React/RCTLinkingManager.h>
// Add this inside `@implementation AppDelegate` above `@end`:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
3. Handle deep links in React Native (With Path Parameters)
import React, { useEffect } from 'react';
import { Linking } from 'react-native';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
import ProfileScreen from './screens/ProfileScreen';
import DetailScreen from './screens/DetailScreen';
const Stack = createStackNavigator();
// Define Deep Linking Configuration
const linking = {
prefixes: ['testapp://', 'https://yourwebsite.com'], // Custom URL scheme & universal links
config: {
initialRouteName: 'HomeScreen',
screens: {
HomeScreen: '',
ProfileScreen: 'profile/:userId',
DetailScreen: 'details/:itemId',
},
},
};
const App = () => {
const navigation = useNavigation(); // Access navigation inside component
useEffect(() => {
const handleDeepLink = (event) => {
const url = event.url;
console.log("Deep Link Received:", url);
if (url) {
try {
const pathParts = new URL(url).pathname.split('/').filter(Boolean);
const screen = pathParts[0];
const param = pathParts[1];
if (screen === "profile" && param) {
navigation.navigate("ProfileScreen", { userId: param });
} else if (screen === "details" && param) {
navigation.navigate("DetailScreen", { itemId: param });
} else {
navigation.navigate("HomeScreen");
}
} catch (error) {
console.error("Error parsing deep link:", error);
}
}
};
// Subscribe to incoming deep links
const subscription = Linking.addEventListener("url", handleDeepLink);
// Handle deep links when the app starts
Linking.getInitialURL().then((url) => {
if (url) handleDeepLink({ url });
});
return () => {
subscription.remove(); // Cleanup on unmount
};
}, [navigation]); // Ensure navigation is available
return (
<NavigationContainer linking={linking}>
<Stack.Navigator initialRouteName="HomeScreen">
<Stack.Screen name="HomeScreen" component={HomeScreen} />
<Stack.Screen name="ProfileScreen" component={ProfileScreen} />
<Stack.Screen name="DetailScreen" component={DetailScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
Here, we’ve set up deep linking for both URI schemes & universal links in our React Native app, allowing users to open screens via deep links.
The linking object defines how deep linking works.
prefixes
– Recognize URL schemes (testapp://)
for custom links, domain (https://yourwebsite.com
) for universal links.config
– Maps URLs to screens (e.g.,testapp://profile/123
navigates toProfileScreen
withuserId: 123
since the URL segment"profile"
matched to theProfileScreen)
Deep linking is handled in two ways:
getInitialURL()
– Runs when the app launches via a deep link, navigating to the correct screen.subscribe()
– Listens for deep links while the app is running, handling navigation instantly.
Inside useEffect
, we use both methods to ensure deep linking works on app launch (getInitialURL()
) and in real-time (subscribe()
).
Step 2: Set Up Universal Links
Unlike URL Schemes, Universal Links require additional server-side configuration.
You need to host an AASA file on your website. This file tells iOS that your app should handle certain links. You will need:
- A web server with SSL (HTTPS).
- The ability to host static JSON files.
1. Steps to create the Apple App Site Association (AASA) File
Below are the steps for hosting the AASA file on your website:
- Access the hosting server and navigate to the directory where your website files are stored.
- Create a sub-directory in the root directory of your website and name the directory as ‘well-known’.
- Create a new file named ‘apple-app-site-association’ & paste the below code into this file.
{
"applinks": {
"apps": [],
"details": [
{
"appIDs": ["YOUR_TEAM_ID.com.testapp.ios"],
"paths": [
"/home",
"/profile/*",
"/details/*"
]
}
]
}
}
(Remember to replace the appIDs with the correct teamID & the bundle Id of your app/apps)
- Don’t append any extension (such as .rtf/.json/.txt, etc.) when saving this AASA file.
- Once verified that the AASA file is correctly formatted and named as ‘apple-app-site-association’, save the changes and confirm that the file is accessible through the following URL:
<YourWebsiteURL>/.well-known/apple-app-site-association
- Note: Please ensure that the file is accessible via a secure HTTPS connection.
3. AASA Validator — You can use an AASA validator tool to check whether your AASA file is correctly configured and available.
- Visit this URL: https://branch.io/resources/aasa-validator/
- Enter your domain (
https://<YourWebsiteURL>/.well-known/apple-app-site-association
). - The tool will confirm whether your file is correctly configured.
4. Enable Associated Domains in Xcode
- Go to Signing & Capabilities → Associated Domains.
- Click + and add Associated Domains as in Figure C. applinks:yourwebsite.com

5. Handle Universal Links in React Native
Modify AppDelegate.m
by adding the below code snippet to handle Universal Links:
// Add this inside `@implementation AppDelegate` above `@end`:
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
Your React Native code for handling deep links remains the same as the URL Schemes.
📌 How to test?
To test deep linking in an iOS simulator, you can use either npx uri-scheme
or Apple’s xcrun simctl openurl
.
Method 1: Using npx uri-scheme
Run the following command in your terminal to test a deep link without installing anything:
npx uri-scheme open testapp://profile/123 --ios
This will open the deep link in the currently running iOS simulator.
It works for both custom URL links (testapp://profile/123
) and Universal links (https://yourwebsite/profile/123
).
Method 2: Using xcrun simctl openurl (Built-in Apple CLI)
For a native testing method, use Apple’s built-in CLI:
xcrun simctl openurl booted testapp://profile/123
This works without installing any npm packages.
⚠️ Remember, for both of the above methods to work in the expected behavior, ensure debug mode(React Native chrome debugger) is off.
When testing Universal Links in the simulator, you can either enter the link in Safari’s address bar or paste the link into the Notes, Messages, or Reminders app and tap it. However, the best way to test Universal Links is by using a real device.
Important Notes About Universal Links
While Universal Links provide a seamless deep linking experience, they do not work in every situation. Here are some key limitations:
- Universal Links won’t work in TestFlight: Universal Links rely on the AASA file, which is only downloaded when the app is installed from the App Store. Since TestFlight isn’t the App Store, it doesn’t fetch the AASA file, making Universal Links unreliable during development. The best way to test is by publishing a beta version to the App Store and verifying the links in a real environment. (As a workaround, you can implement a URI scheme for each testflight version of your app)
- Some apps disable Universal Links in WebViews, like Facebook and Instagram. (The link may open the webpage in the webView instead of opening the app even when it’s installed in the device as they don’t want users to leave their platform)
- Safari may not always open Universal Links correctly. It’s best to test using links by tapping from Notes, iMessage, or any other app.
- Universal Links don’t work with links from the same domain. (if a user is already on
yourwebsite.com
in Safari and taps another Universal Link (https://yourwebsite.com/offer
), it won’t open the app. Instead, it will just load the webpage) - To trigger Universal Links and work in the expected behaviour, a user action is required. (JavaScript triggers such as
window.onload
won’t work)
📌 What About Deferred Deep Linking?
Universal Links work great — but only if the app is installed. What if the app isn’t installed when the user clicks a deep link? It just directs the user to the AppStore, but the connection with the link is lost thereafter. Hence, the user needs to tap the same link again to navigate to the intended screen. This is where the deferred deep linking approach becomes useful.
Deferred deep linking ensures that:
- When a user clicks a deep link, if the app isn’t installed, they’re redirected to the App Store. and after installing & opening the app from the App Store, they land on the intended screen.
As per my knowledge, iOS doesn’t support this natively, so you need a 3rd party solution like Branch.io, AppsFlyer, etc. (Please do your own research to find the best platform that gives this service as per your requirement).
📌 Final Thoughts
Deep linking is essential for seamless navigation and user experience. URL Schemes are great for quick deep linking, while Universal Links provide a secure, user-friendly experience.
My recommendation: Use both the URL scheme & universal links together for maximum compatibility! 🚀
Now that you understand everything about deep linking, it’s time to implement it in your React Native iOS app.
Happy coding!
While I’ve shared my experience and best practices for implementing deep linking in iOS with React Native, technology evolves, and there might be details I missed or improvements that can be made. If you notice any mistakes, outdated information or have better solutions, I’d love to hear from you! Feel free to share your thoughts or feedback in the comments section. Thank you!