react native deep linking
react native
expo linking
universal links
mobile app development

A Practical Guide to React Native Deep Linking for iOS and Android

A Practical Guide to React Native Deep Linking for iOS and Android

React Native deep linking is all about using a URL to send a user to a specific screen inside your mobile app, instead of just dumping them on the home screen. It’s the bridge that connects your app to the wider web, creating a seamless path from an email, a social media post, or a web ad directly to the content your user actually wants to see.

This isn't just a "nice-to-have" feature; it's a fundamental tool for modern app marketing and user engagement.

Why Deep Linking Is Essential for Modern React Native Apps

Ad, Email, and Social icons direct users via deep linking to a mobile app, showing growth.

Getting someone to install your app is only the first step. The real challenge is keeping them engaged, and that's where React Native deep linking completely changes the game. It turns what could be a clunky, multi-step process into a single, fluid action.

Think about it from your user's perspective. They see an ad for a specific pair of shoes. Without a deep link, they tap the ad, get sent to the App Store, download the app, open it to the home screen... and then have to go searching for those shoes all over again. You've almost certainly lost them.

Now, imagine the same scenario with deep linking. The user taps the ad and lands directly on the product page for those shoes inside your app. The user’s intent is met instantly, the friction is gone, and the chance of a conversion goes through the roof.

To really see the difference, let's compare the two user journeys.

Deep Linking vs Standard App Entry

Metric Standard App Entry With Deep Linking
User Journey Tap ad → App Store → Install → Open app → Manually find content Tap ad → Land directly on relevant content
User Friction High (multiple steps, potential for drop-off) Minimal (intent is met immediately)
Conversion Rate Lower (user may give up) Significantly higher
Experience Disjointed and frustrating Seamless and intuitive

As you can see, the difference is stark. Deep linking directly translates to a better user experience and stronger business results.

Boosting Engagement and Conversions

At its core, deep linking is about eliminating friction. By taking users exactly where they want to go, you create a much more positive and effective experience that directly impacts your key metrics.

  • Smarter User Onboarding: Guide new users to a specific tutorial or feature screen immediately after they install the app.
  • Higher Conversion Rates: Link your marketing campaigns directly to the relevant product or checkout page. No more lost sales.
  • Better Re-engagement: Use push notifications or email campaigns to pull inactive users back to specific, high-value content within the app.

This direct path is what separates a good app experience from a great one. It shows you respect the user's time and intent, building trust right from the first click.

Navigating the Privacy-First World

The mobile ecosystem has changed dramatically with privacy updates like Apple's App Tracking Transparency (ATT). Traditional user tracking is becoming less effective, forcing developers to find better, privacy-compliant ways to create personalized experiences. Deep linking is perfectly suited for this new reality.

With projections showing that only 15-20% of iOS users will opt into tracking by 2026, old marketing methods are quickly becoming obsolete. In contrast, apps using deep links are seeing over 30% click-to-install rates—a huge leap from the typical 5% you get from a generic App Store link. This is because the focus shifts from long-term tracking to serving the user's immediate need.

A deep link doesn't care who the user is; it only cares where the user wants to go. This focus on intent over identity is perfectly aligned with modern privacy standards.

If you're just building out your first app, you might want to start with a solid guide on getting started with React Native to get your foundation right. Once you're comfortable, you can dive into powerful features like this one.

Getting the Core Deep Linking Concepts Straight

Diagram illustrating URI schemes and universal app links for mobile deep linking and deferred installation.

Before we touch a single line of code, let's get the terminology down. Honestly, understanding the difference between a URI scheme and a Universal Link is half the battle. Getting this right from the start will save you a world of headaches later on.

At its core, a deep link is just a special kind of URL. When a user taps it, the operating system (iOS or Android) intercepts it. Instead of just opening a web browser, the OS recognizes the link is meant for your app, launches it, and hands it the URL data. Your app can then use that data to navigate the user directly to the right screen. This simple but powerful process is the foundation of any React Native deep linking setup.

URI Schemes: The Classic Approach

The original way to handle deep linking was through custom URI schemes. Think of it as creating a private protocol for your app. Instead of https://, your links start with something unique, like myapp://. You register this custom scheme, and suddenly a link like myapp://products/12345 knows to open your app and head to the product screen for item 12345.

It's pretty straightforward, but there are a couple of major gotchas. First, these schemes aren't guaranteed to be unique. Another developer could have the same bright idea and also register myapp://. If a user has both apps, their phone gets confused and has to ask which one to open, which totally ruins the seamless flow.

Even worse, if the user doesn't have your app installed, clicking a myapp:// link does absolutely nothing. It’s a dead end. Because of these limitations, URI schemes are mostly considered a legacy method or a fallback at best.

Universal Links and App Links: The Modern Standard

To fix the problems with URI schemes, Apple and Google introduced much smarter, more secure alternatives: Universal Links for iOS and App Links for Android. Their biggest win is that they use standard https:// web URLs that you already own.

This means a single link—https://www.yourapp.com/products/12345—can do two different things perfectly.

  • If the app is installed: The OS opens your app directly, passing along the /products/12345 path for you to handle.
  • If the app is not installed: The link works like any normal URL and opens your website in the browser. No dead ends.

This magic works because you prove ownership of the domain by hosting a small configuration file on your website (an apple-app-site-association file for iOS and assetlinks.json for Android). This verification step prevents other apps from hijacking your links and creates a much safer, more reliable experience for users. For any serious React Native deep linking strategy today, this is the way to go.

A key takeaway is that Universal Links and App Links are not just links; they are a trust mechanism. By proving you own both the app and the website, you earn the right to create a seamless, verified user journey.

Standard vs. Deferred Deep Linking

There's one final distinction that’s incredibly important: standard versus deferred deep linking. Everything we’ve talked about so far is standard deep linking. It works beautifully, but only if the user already has your app on their device.

Deferred deep linking handles the scenario where a new user clicks a link. It’s a game-changer for marketing and user acquisition. Here’s the flow:

  1. A potential user clicks a link to a specific product you’re promoting.
  2. The system sees they don't have the app and sends them to the App Store or Google Play Store to download it.
  3. Here’s the clever part: after they install and open the app for the first time, the original link context is "remembered."
  4. Your app receives that context and automatically navigates them to the exact product page they were interested in from the start.

This creates an amazing first impression and can drastically improve the conversion rates of your campaigns. While React Native doesn't handle this out of the box, several excellent third-party services can add this "deferred" capability to your app.

Implementing Deep Links with Expo and React Navigation

Diagram illustrates Expo React Native deep linking flow, showing app.json scheme, NavigationContainer, and screen routes like ProductScreen and Home.

For anyone building a React Native app today, the combination of Expo and React Navigation is often the path of least resistance. This stack is so popular because it smooths over the rough edges of native development, and that's especially true for React Native deep linking. It handles a ton of the low-level native configuration for you.

Let’s walk through how to get it done.

It all starts with giving your app a unique URL identifier, known as a scheme. This is what tells iOS or Android that a link like myapp:// should open your application, not a web browser. In an Expo project, this is ridiculously easy and managed right in your app.json or app.config.js file.

Just pop a scheme property into the expo object.

{ "expo": { "name": "MyAwesomeApp", "slug": "myawesomeapp", "scheme": "myawesomeapp" } }

With that one line, myawesomeapp:// is now officially your app's deep link prefix. It’s a small change, but it’s the foundation for everything else.

Configuring React Navigation to Handle Links

Okay, so your app now recognizes its own URL scheme. The next piece of the puzzle is teaching React Navigation what to do when it receives one of these links. This is all handled by passing a special linking configuration object to your root <NavigationContainer />. Think of this object as a routing map that translates URL patterns into specific screen navigations.

We saw React Native's growth explode in 2025, making deep linking a must-have feature as framework downloads soared. This wasn't just a trend; it was a response to real-world needs. Apps using deep linking saw 30% higher click-to-install rates, a huge win for user acquisition. Now, as 2026 continues and Expo downloads are peaking, deep linking is still the secret to a 64% uplift in conversions. It's how Nerdify clients are building stickier, more engaging apps.

Let's get practical. Imagine a common scenario: you have Home and ProductDetail screens, and you want a link like myawesomeapp://products/123 to jump directly to the detail screen for the product with ID 123.

Here’s what your linking configuration would look like:

const linking = { prefixes: ['myawesomeapp://'], config: { screens: { Home: 'home', ProductDetail: { path: 'products/:id', parse: { id: (id) => product-${id}, }, }, }, }, };

This little block of code is powerful. It tells React Navigation to watch for URLs starting with myawesomeapp://. More importantly, it maps the URL path products/:id to your ProductDetail screen. The :id part is a wildcard, and React Navigation automatically extracts the value (123 in our example) and passes it to your screen as a route parameter. You can even use the parse function to clean up or transform parameters on the fly.

To help clarify these properties, here's a quick breakdown of what you'll be using in the linking object.

Expo Linking Configuration Properties

Property Purpose Example Value
prefixes An array of URL prefixes your app should handle. This must include your custom scheme. ['myawesomeapp://', 'https://myawesomeapp.com']
config An object that maps URL paths to your app's navigation structure (screens). { screens: { ... } }
path Defines the URL path segment that corresponds to a specific screen. Can include dynamic params like :id. 'products/:id'
parse An optional function to transform a URL parameter before it's passed to the screen as a prop. { id: (id) => Number(id) }

This configuration is the core of your deep linking logic, connecting external URLs to the internal navigation state of your app.

Testing Your Deep Links with Expo Go

Here's one of my favorite things about the Expo workflow: you can test your deep links instantly without ever building a standalone binary. The Expo Go app on your phone or simulator is all you need.

The Expo CLI gives you a command specifically for this.

npx uri-scheme open myawesomeapp://products/456 --ios

When you run that in your terminal, it perfectly mimics a user tapping that link. Expo Go will pop open (or come to the foreground) and navigate straight to the product detail screen for item 456. Swap in the --android flag to test on Android. This quick feedback loop is a lifesaver for ironing out your routing logic.

Preparing for Standalone App Builds

Testing in Expo Go is fantastic for development, but eventually, you'll build a real app for the App Store and Google Play. The good news? You've already done the hard part.

When you run your build command (eas build), Expo automatically takes the scheme from your app.json and configures the native project files for you—specifically the Info.plist on iOS and AndroidManifest.xml on Android. This means the deep links you perfected in Expo Go will work exactly the same in your production app, with zero extra native code required.

If you want to go deeper into the build process itself, our guide on Expo app development covers that and more.

Key Takeaway: The beauty of using Expo with React Navigation is how much it abstracts away. You define a scheme and a JavaScript-based linking config, and you get a robust deep linking system that works across both iOS and Android. No wrestling with native code needed.

Configuring Links for Bare React Native Projects

When you eject from Expo or start a bare React Native project from scratch, you get a lot more power under the hood. The trade-off? You're now in charge of the native configuration, which for React Native deep linking, means diving into both Xcode and Android Studio.

Don't let that intimidate you. It's less about writing complex native code and more about knowing exactly which files to edit and what settings to toggle. Let's walk through setting up the modern, secure standards: Universal Links for iOS and App Links for Android.

Setting Up Universal Links for iOS

On iOS, Universal Links are the way to go. They create a secure, trusted bridge between your website and your app, using your own domain. Getting them to work involves two main parts: adding a special file to your web server and then tweaking your Xcode project.

Your first task is to create a file named apple-app-site-association—and yes, it has no file extension. This is a simple JSON file that tells iOS which paths on your domain your app should handle.

{ "applinks": { "apps": [], "details": [ { "appID": "YOUR_TEAM_ID.com.yourcompany.yourapp", "paths": ["/products/", "/profile/"] } ] } } Here, the appID is a combination of your Apple Developer Team ID and your app’s bundle identifier. The paths array is where you define the URL patterns your app can open, and the wildcard * is incredibly helpful for matching dynamic routes.

You'll need to host this file on your domain over HTTPS. Apple will look for it in one of two places:

  • https://yourdomain.com/.well-known/apple-app-site-association
  • https://yourdomain.com/apple-app-site-association

Critical Tip: I've seen this trip up countless developers: your server must serve this file with the application/json content type and without any redirects. A subtle server misconfiguration is the most common reason Universal Links fail to validate.

Once that file is live, it’s time to pop open Xcode. Go to your project, click the app target, and navigate to the Signing & Capabilities tab. Hit the + button to add the Associated Domains capability. In the domains list that appears, add an entry for your website, making sure to prefix it with applinks:.

applinks:yourdomain.com

Finally, you’ll need to add a bit of boilerplate code to your AppDelegate.mm file. This ensures that any incoming links are correctly passed along to the React Native runtime, where React Navigation or the Linking module can take over.

Configuring App Links on Android

The setup for Android App Links is very similar to its iOS counterpart. You'll use a JSON file to verify that you own the domain, and then you'll make some small but crucial changes to your native Android project files.

Start by creating an assetlinks.json file. This is Android's version of the AASA file, declaring the association between your website and your app.

[ { "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.yourcompany.yourapp", "sha256_cert_fingerprints": ["YOUR_SHA256_FINGERPRINT"] } } ]

Make sure the package_name is an exact match. The sha256_cert_fingerprints value comes from your app's signing certificate. Remember, you'll have different fingerprints for debug and release builds, so double-check that you're using the correct one for your production app.

This file needs to be hosted at https://yourdomain.com/.well-known/assetlinks.json.

Next, you have to tell your app to listen for these links. Open your AndroidManifest.xml (found in android/app/src/main/) and find the main <activity> tag. Inside it, add this intent filter.

That `android:autoVerify="true"` attribute is the magic sauce. It instructs the Android OS to verify the `assetlinks.json` file when the app is installed. If everything checks out, the OS will automatically open your app for any matching URLs instead of the browser.

As a final pro-tip, I always set the launchMode for the main activity to singleTask in the AndroidManifest.xml. This small change prevents a new instance of your app from launching every time a deep link is opened, which makes for a much cleaner and more predictable user experience.

This native configuration is a key part of the puzzle. For a broader look at the entire development journey, our guide on building apps with React Native covers the full lifecycle from setup to deployment.

Advanced Deep Linking Techniques and Security

A technical diagram illustrates deep linking implementation steps, from command line to security validation.

Alright, so you've got your basic deep links navigating to the right screens. That’s a huge first step, but the journey isn't over. To build a truly production-ready system, we need to think beyond the happy path and focus on rigorous testing, security, and the messy reality of edge cases.

This is what separates a quick prototype from a polished, reliable app that users can trust.

Efficiently Testing Your Links

At first, clicking links from your notes app or an email to test your setup seems fine. But you'll quickly realize how slow and inefficient that is. A solid React Native deep linking setup needs automated, repeatable testing.

This is where the command line becomes your best friend. You can trigger a deep link directly from your terminal, which is a game-changer for development and essential for any CI/CD pipeline. It gives you a fast, scriptable way to confirm your routing logic works every time.

  • For the iOS Simulator: Pop open your terminal and use the xcrun simctl openurl command. Just point it at the booted simulator and feed it the URL. xcrun simctl openurl booted "yourapp://products/99"

  • For an Android Emulator or Device: The go-to command is adb shell am start. It’s a bit more of a mouthful but does the same job by telling the Android activity manager to fire an intent with your URL. adb shell am start -W -a android.intent.action.VIEW -d "https://yourdomain.com/products/99" com.yourcompany.yourapp

Using these commands lets you test every state—app open, app in the background, and app completely closed. Mastering this is non-negotiable for building a dependable system.

Fortifying Your Deep Links Against Threats

When you expose your app to external URLs, you’re essentially opening a new door. If you don't secure it properly, you're creating a potential entry point for bad actors. Security can't be an afterthought.

Your first line of defense is using Universal Links on iOS and App Links on Android. Because they require you to verify your domain (with the AASA and assetlinks.json files), they solve the problem of scheme hijacking right out of the box. That’s where a malicious app registers the same custom scheme as yours (e.g., yourapp://) hoping to intercept sensitive data.

The most critical security measure, however, happens inside your app: always validate and sanitize incoming URL parameters. Never, ever trust that a link like yourapp://user/123 actually contains a clean ID. Someone could easily craft a malicious link with injected code or unexpected values to try and break things.

Before you use any parameter from a URL, clean it up. If you're expecting a number, parse it and verify it's a valid integer. If it's a string, sanitize it to strip out anything that looks like a potential injection attack. These simple checks can prevent a catastrophic security flaw. For a more thorough look at this topic, our guide on mobile app security best practices is a great resource.

Handling Edge Cases and User Experience

A professional app anticipates what happens when things go wrong. What if a user clicks a link to a product that was just deleted? Or what if a logged-out user tries to access a screen that requires authentication?

Thinking through these flows is crucial for a good user experience.

  1. Content Not Found: A user taps a link for yourproduct/deleted-item. Instead of showing a blank screen or, worse, crashing the app, you should catch the invalid ID. From there, you can navigate them to a custom "Not Found" screen or perhaps redirect them to a parent category. It turns a dead end into a helpful detour.

  2. Authentication Required: A user who isn't logged in gets a link to their profile (/profile). The ideal flow is to intercept this navigation attempt, redirect them to your login screen, and—this is the important part—remember where they were trying to go. Once they log in successfully, your app should immediately take them to their profile. It’s a seamless experience that just works.

By planning for these common edge cases, you ensure your app feels stable and user-friendly, no matter what curveballs are thrown its way.

Common Questions (and Answers) About React Native Deep Linking

Once you start implementing deep links, you'll inevitably run into a few head-scratchers. It's just part of the process. I've seen the same questions pop up time and again, so let's walk through the most common ones.

What Happens if a Deep Link Is Opened When the App Is Closed?

This is what we call a "cold start," and thankfully, modern tools handle it pretty well. When a user taps a link that opens your app for the very first time, the operating system holds onto that URL and passes it to your app as it launches.

If you're using a library like React Navigation, its linking configuration is built to catch this. It automatically checks for an initial URL and sends the user to the right screen based on the mapping you've set up. If you need more control, you can always grab the URL yourself with Linking.getInitialURL() from React Native or useURL from expo-linking to run some custom logic first.

The most important thing is to make sure your NavigationContainer wraps your entire app structure. With the linking prop configured, it can properly intercept that startup URL and handle the initial navigation for you.

URI Schemes vs. Universal Links: What's the Real Difference?

It’s tempting to start with a custom URI scheme like myapp://, and for good reason—they’re simple to implement. The major downside, however, is that they aren't guaranteed to be unique. Another app could register the same myapp:// scheme, which would cause the OS to show a popup asking the user which app to open. That completely breaks the seamless experience you're going for.

Universal Links (for iOS) and App Links (for Android) are the industry-standard solution. They use standard https:// URLs that point to a domain you own. You prove ownership to the OS by hosting a special verification file on your website. This removes any ambiguity, ensures your app is the only one that opens, and makes your links far more secure.

Help! My Universal Link Opens in Safari Instead of My App.

Ah, the classic Universal Link headache. I’ve been there. If your link opens in the browser, it’s almost always one of a few things:

  • Your AASA file is wrong. The apple-app-site-association file on your server must be perfect. It has to be valid JSON, served over HTTPS with an application/json content-type header, and—this is a big one—it cannot have any server-side redirects.
  • Your Xcode setup is off. In Xcode, double-check that your "Associated Domains" entitlement is there and formatted correctly: applinks:yourdomain.com.
  • You're testing it incorrectly. This trips people up all the time. You cannot test a Universal Link by pasting it directly into the Safari address bar. It has to be tapped from another app, like Notes, Mail, or Messages, for the OS to intercept it properly.

How Can I Test My Deep Links Without Manually Clicking Them?

Manually building your app and then finding a place to click a link is way too slow for active development. You need a faster feedback loop, and the command line is your best tool for the job.

  • For Expo Go: The Expo CLI makes this super easy. Just run npx uri-scheme open 'myapp://screen' --ios to simulate a link tap on your connected device or simulator.
  • For a Bare React Native App:
    • iOS Simulator: Use xcrun simctl openurl <simulator_id> 'myapp://screen'
    • Android Emulator: Use the adb command adb shell am start -a android.intent.action.VIEW -d 'https://yourdomain.com/path'

These commands let you fire off test links in seconds, so you can quickly confirm your routing logic is working as expected.