Deep Linking in React Native

Dive into the sea of deep linking. We have some code and a few examples to keep you afloat.

Update: We’ve published part two of this article, focusing on animating your scroll position after deep linking. Find it on Deep Linking in React Native — Scroll to Element.

What and why? 👀

For starters, what exactly does it mean to deep link? It’s a term that represents the action of navigating to a specific screen or a resource inside an app, using a URI. The mentioned screen is usually located deeper in the stack. Deep links connect users to relevant content, using a single navigation step, in a way that’s intuitive for them. The practice can take various forms. For example, pressing an image which navigates you to a post on the Facebook app, ads that navigate you to the App Store or a promo email leading the way to an app.

We can differentiate internal and external deep links (not the official classification, but you’ll get the point). Internal links navigate users to a specific screen of the same app, while external ones navigate users from your app into another.

Let’s say you have some sort of geolocation support but only in the form of a small map, appearing in your app. So, you want users to have the ability to deep-link into Google Maps. Or you just don’t want to go to web view in your app but open a certain URI in the device’s default browser app instead. This would be external deep linking.

An example of external deep linking.

An example for an internal deep link would be pressing a notification bubble which navigates to a specific screen (someone liked your photo → navigate user to photo details screen). So, if the whole deep linking thing is happening inside a single app, it would be considered an internal link.

An example of internal deep linking.

In this post, I’ll explain these principles on a simple notification example. When the users press a notification, you want to navigate them into a corresponding screen, in most cases stacking a bunch of screens under it in a logical order. One of the problems you may encounter while using internal linking is passing the data to an inner screen. Since the user is not navigating and stacking one screen at a time (and passing the required data accordingly), you’ll have to find a way to get specific data that needs to be presented directly on that screen. Let me give you an example here.

You have Instagram running in the background and receive a notification saying someone liked your photo. After pressing on that notification, Instagram will stack the picture details screen (let’s call it that) on top of the screen that the app was on in the background. Similarly, with Instagram cold starting, this will result in the picture details screen being stacked on the homepage of the app. How this will be handled in your app is solely up to you, and perhaps the designer.

When defining routes, each screen should have an assigned path property, which looks like this example, or like this example/:exampleId, with exampleId as a parameter. So, to solve the data passing problem, you’d want to use a deep link URI. This not only decides which screens to navigate to but also passes various data required on that screen. You can access data passed as a part of the link in your components via the navigation prop, but more about that later. The idea here is to get the id for that screen from a deep link and to fetch the necessary data from the backend.

You can also use deep links to tell the app which tab on your tab navigator to select as active. Imagine an app with a bottom tab navigation and each tab having a stack navigator. Each tab has its own route as well. With deep links, you can define which tab to set as active and stack more screens from that tab on top, all in one route. That link could look something like this demo://secondtabname/one/two/:Id.

So, you can combine stacking, switching tabs, passing params — go crazy here!

Implementation 🚧

Let’s get more technical! For deep links to be recognized by a device, you’ll need to register them separately for each platform. Just as a side note: I’m using the following versions: react-native 0.61.5, react-navigation 4.0.10 and react-navigation-stack 2.0.13.

Native pre-requirements

iOS requires adding a URL types identifier and a scheme in your Xcode project:

After that, you’ll need to modify the AppDelegate file by adding the following code:

#import <React/RCTLinkingManager.h>

// ...
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation

  return [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];

For Android, open AndroidManifest.xml. Besides the scheme, you need to register each host and remember to add new hosts upon further implementation in the React Native app ⚠️. Here’s an example snippet, lines to add are 22–30:

React Native

The next thing you have to do is to open your uppermost React component (or the one that’s a parent to those that need deep linking abilities). In my case, that was the App.js file which is rendering the AppNavigator. There you have to add the uriPrefix prop with your deep linking scheme:

render() {
   return (
         // ...

Adding routes to the navigator is the next step. As mentioned before, this can work on each type of the react-navigation navigators.

Just as a quick overview: my demo app contains an outer SwitchNavigator, which is made of the Login screen and the inner StackNavigator named Home, whose structure is visible in a code snippet, seen a few lines down. There is also a link to a GitHub repo at the end of this paragraph.

For the createStackNavigator (a method imported from react-navigation-stack, used to define the interactions with screens getting stacked on one another) you’d get something like this:

This tells the react-navigation-stack how to stack screens upon deep linking. In our Demo app, we have 2 navigators: the outer switch and the inner stack. So, if we went to demo://home/second/42, visually we’d get the following:

Keep in mind that the app cold started, which produced some lag during stacking.

So, what exactly happened? Home told the navigator to switch from the Login to the Home screen in the switch navigator. Since Home is a stack navigator, it opened on its initial route, which is the First screen. Finally, Second stacked the Second screen on top. Because the first step is done in the switch navigator, and the rest in a stack navigator, we were only able to swipe back one step. You can access the parameter passed through the link (in our case it’s number 42) in the following way:


For the last step, let’s connect deep linking to push notifications. You can use React Native’s Linking interface, more precisely its openURL method. This should be executed when the user presses the notification bubble on their device. To get that function, you’ll need some sort of a listener/handler, depending on which push notification service you’re using (or perhaps you’ve implemented your own). I was using One Signal, so the code in the component that is wrapping the whole app was the following ⬇️. This is only necessary for iOS, hence the platform check:

OneSignal.addEventListener(opened, this.onOpened);

// of the OneSignal configuration...

onOpened(openResult) {
   if (Platform.OS === "ios") {
             .catch(err  =>
               console.error(An error occurred, err)

You’re good to go 🚀. Check my GitHub for the source code:

Where to go from here? 🚗

Something I’ve neglected to mention in this post is a nice UX spice up – scrolling to specific coordinates of ScrollView when deep linking, e.g. the comment thread opens and the UI automatically scrolls to a new reply on your comment.

You can also take a closer look at universal links, made by Apple, for devices running iOS. They differ from classic deep links because they don’t use URI links but match a set of web pages to locations in-app. So, when a user opens a matched web page, he will automatically be redirected to the app. Universal links are exclusive to iOS devices and work even when the app is not installed. However, I would not really recommend implementing support for this in your React Native app. It can bring potential complications and not-so-many benefits for a React Native app. If you decide to go for it, read all the documentation carefully, it’s always a good idea.

Have fun! 😊

Leave a comment Be the first!