React Native

React Native Deep Linking Tutorial

Complete guide to deferred deep linking in React Native with Redirectly: pure TypeScript SDK, works with Expo and bare React Native. Includes iOS Universal Links, Android App Links, install attribution, React Navigation v6 integration, and testing.

Introduction

Why Deep Linking Matters for React Native Apps

Deep linking allows users to jump directly to specific content in your app from external sources—emails, SMS, web pages, ads, and social media. Instead of landing on the app home screen, users land exactly where you want them, dramatically improving user experience and conversion rates.

Deferred deep linking takes this further: links work even if the app isn't installed yet. When a user clicks a link, we detect the install, and after the app opens for the first time, the SDK retrieves the original link data and routes the user to the correct destination. This is crucial for acquisition campaigns, app launch promotions, and referral programs.

The React Native Redirectly SDK (react-native-redirectly) is a pure TypeScript implementation with full type definitions. It works seamlessly with both bare React Native and Expo, and handles the entire deep linking flow—no native code changes required for the SDK itself.

Deferred deep linking

Links survive app install and route users to the right content.

Install attribution

Match installs to link clicks and campaigns.

TypeScript

Full type definitions, works with Expo and bare RN.

Prerequisites

Before you start

This guide assumes you have a React Native project set up and running. Here's what you need:

  • React Native 0.61+ (0.70+ recommended)
  • React Navigation v5+ (v6 recommended for best integration)
  • Redirectly account and API key (sign up free at redirectly.app)
  • Redirectly subdomain (e.g., myapp.redirectly.app)
  • For bare RN: Xcode (iOS) and Android Studio (Android)
  • For Expo: EAS Build (optional, for production deep linking)
  • AsyncStorage (optional but recommended for install attribution persistence)

This guide covers both Expo and bare React Native. Some sections apply only to bare RN (iOS Universal Links, Android App Links). Expo users can skip directly to the Expo Configuration section.

Step 1

iOS Universal Links Setup

iOS uses Universal Links to route deep links to your app. Universal Links require configuration in Xcode, an entitlements file, and an AASA (apple-app-site-association) file hosted on your domain. Redirectly automatically hosts the AASA file for your subdomain.

Step 1a: Xcode Associated Domains Configuration

Open your project in Xcode, select the target, and go to Signing & Capabilities. Add the Associated Domains capability (+ Capability button).

Associated Domains Capability

applinks:myapp.redirectly.app
webcredentials:myapp.redirectly.app

Replace myapp with your Redirectly subdomain.

Step 1b: Entitlements File

The associated domains must also be declared in your entitlements file. Xcode usually manages this, but you can verify or manually add to your {ProjectName}.entitlements file:

{ProjectName}.entitlements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>com.apple.developer.associated-domains</key>
  <array>
    <string>applinks:myapp.redirectly.app</string>
    <string>webcredentials:myapp.redirectly.app</string>
  </array>
</dict>
</plist>

Replace myapp with your Redirectly subdomain.

Step 1c: AppDelegate Configuration (Optional)

If you're using React Navigation, most handling is done in the Navigation linking config (see React Navigation section below). However, you may need to handle the app delegate method for certain scenarios:

AppDelegate.swift (Optional)

import UIKit
import React

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
    // React Navigation and Redirectly SDK handle this via linking config
    return true
  }

  func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    // Handle universal links - React Navigation handles this
    return RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
  }
  // ... rest of AppDelegate
}

Step 1d: AASA Verification

Redirectly automatically hosts the apple-app-site-association file at https://myapp.redirectly.app/.well-known/apple-app-site-association. Verify it's correct and properly signed using our AASA validator tool.

Step 2

Android App Links Setup

Android uses App Links to route deep links to your app. Unlike Universal Links, App Links require digital asset verification via an assetlinks.json file. Redirectly hosts this file for your subdomain, and you declare the link intent filter in AndroidManifest.xml.

Step 2a: Intent Filter in AndroidManifest.xml

Add an intent filter to your MainActivity in android/app/src/main/AndroidManifest.xml:

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.myapp">
  <application>
    <activity
      android:name=".MainActivity"
      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="myapp.redirectly.app" />
      </intent-filter>
    </activity>
  </application>
</manifest>

Replace myapp.redirectly.app with your Redirectly subdomain. The android:autoVerify="true" attribute triggers automatic verification of the assetlinks.json file.

Step 2b: assetlinks.json Verification

Redirectly automatically hosts the assetlinks.json file at https://myapp.redirectly.app/.well-known/assetlinks.json. Verify it's correct using our assetlinks validator tool. You'll need your app's signing certificate SHA256 fingerprint, which you can find in your Android keystore.

Get your SHA256 fingerprint

keytool -list -v -keystore android/app/debug.keystore -alias androiddebugkey -storepass android -keypass android

For release builds, use your release keystore path and credentials.

Step 3

SDK Installation & Initialization

The react-native-redirectly SDK is pure TypeScript. Install it via npm or yarn, then initialize with your API key at app startup.

Install the SDK

npm or yarn

npm install react-native-redirectly
# or
yarn add react-native-redirectly

Initialize Redirectly

Initialize the SDK at your app's entry point (App.tsx or index.js):

App.tsx

import React, { useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { Redirectly } from 'react-native-redirectly';
import AsyncStorage from '@react-native-async-storage/async-storage';
import RootStack from './navigation/RootStack';

export default function App() {
  useEffect(() => {
    const initializeRedirectly = async () => {
      try {
        const redirectly = Redirectly.getInstance();
        await redirectly.initialize({
          apiKey: 'your-api-key',
          enableDebugLogging: __DEV__,
          asyncStorage: AsyncStorage, // Optional but recommended
        });

        // Listen for install attribution
        redirectly.onAppInstalled((installData) => {
          if (installData.matched) {
            console.log(
              'Install attributed to:',
              installData.matchedClick?.slug
            );
            // Store or send to analytics
          }
        });

        // Listen for incoming deep links
        redirectly.onDeepLink((deepLink) => {
          console.log('Deep link received:', deepLink);
          // Handle navigation - integrate with React Navigation
        });
      } catch (error) {
        console.error('Failed to initialize Redirectly:', error);
      }
    };

    initializeRedirectly();
  }, []);

  return (
    <NavigationContainer>
      <RootStack />
    </NavigationContainer>
  );
}

For Expo

The SDK works the same in Expo. Install and initialize identically. For production Expo apps, configure deep linking in your app.json (see the Expo Configuration section below).

Step 4

React Navigation v6 Integration

React Navigation v6 provides a powerful linking configuration system. You declare your deep link URL scheme and map paths to screen names. Deep links are automatically matched and your app navigates to the correct screen.

Configure React Navigation Linking

Create a linking configuration that maps your Redirectly subdomain to screen routes:

navigation/linking.ts

import * as Linking from 'expo-linking';

const prefix = Linking.createURL('/');

export const linking = {
  prefixes: [
    prefix,
    'myapp.redirectly.app',
    'https://myapp.redirectly.app',
    'myapp://',
  ],
  config: {
    screens: {
      Home: '',
      Profile: 'profile/:userId',
      Product: 'product/:productId',
      Campaign: 'campaign/:campaignId',
      NotFound: '*',
    },
  },
};

Replace myapp with your Redirectly subdomain. Add more routes and parameters as needed.

Use Linking in NavigationContainer

Pass the linking configuration to your NavigationContainer:

App.tsx

import React, { useEffect, useState, useRef } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { Redirectly } from 'react-native-redirectly';
import { linking } from './navigation/linking';
import RootStack from './navigation/RootStack';

export default function App() {
  const navigationRef = useRef(null);
  const [isReady, setIsReady] = useState(false);
  const [initialRoute, setInitialRoute] = useState(null);

  useEffect(() => {
    const initializeRedirectly = async () => {
      try {
        const redirectly = Redirectly.getInstance();
        await redirectly.initialize({
          apiKey: 'your-api-key',
          enableDebugLogging: __DEV__,
        });

        // Handle deferred deep link on first app open
        redirectly.onAppInstalled((installData) => {
          if (installData.matched && installData.deferred?.url) {
            console.log('Deferred link:', installData.deferred.url);
            // Navigate to the deferred deep link
            if (navigationRef.current) {
              const state = navigationRef.current?.getRootState();
              const url = installData.deferred.url;
              navigationRef.current?.navigate(...); // Or use linking.parse()
            }
          }
        });

        setIsReady(true);
      } catch (error) {
        console.error('Failed to initialize Redirectly:', error);
        setIsReady(true);
      }
    };

    initializeRedirectly();
  }, []);

  if (!isReady) return null;

  return (
    <NavigationContainer
      ref={navigationRef}
      linking={linking}
      fallback={<LoadingScreen />}
    >
      <RootStack />
    </NavigationContainer>
  );
}
Step 6

Testing & Debugging

Test deep links on simulators, emulators, and real devices using platform-specific tools. Enable debug logging in the SDK to troubleshoot issues.

iOS Testing (Simulator & Device)

Test via xcrun (Simulator)

xcrun simctl openurl booted "https://myapp.redirectly.app/product/123"

Replace myapp with your subdomain.

Test via Safari (Real Device)

Open Safari on your device and navigate to your Redirectly subdomain URL directly, or create an HTML page with a link to test.

Android Testing (Emulator & Device)

Test via adb (Emulator & Device)

adb shell am start -a android.intent.action.VIEW -d "https://myapp.redirectly.app/product/123"

Replace myapp with your subdomain.

View adb logs

adb logcat | grep -i "redirectly"
# or filter by tag
adb logcat -s "RedirectlySDK"

Enable Debug Logging

Set enableDebugLogging to true when initializing the SDK to see detailed logs:

Debug Mode

const redirectly = Redirectly.getInstance();
await redirectly.initialize({
  apiKey: 'your-api-key',
  enableDebugLogging: true, // Enable for development
});

Common Issues

  • iOS: Links not opening app

    Check Associated Domains in Signing & Capabilities. Verify AASA file using the AASA validator tool.

  • Android: Links not opening app

    Verify intent filter autoVerify attribute is present. Check assetlinks.json with the assetlinks validator.

  • Deep link data not received

    Enable debug logging and check console output. Ensure your linking config matches your URL structure.

  • Deferred link data missing

    Ensure AsyncStorage is configured for install tracking. Uninstall and reinstall the app to test first-open.

Expo

Expo Configuration

Redirectly works seamlessly with Expo. The SDK is pure TypeScript, so it works out of the box. For production Expo apps, configure deep linking in your app.json so the Expo build system registers your deep link handler.

Configure app.json

Add the deep linking configuration to your Expo app.json to register your Redirectly subdomain:

app.json

{
  "expo": {
    "scheme": "myapp",
    "plugins": [
      [
        "expo-build-properties",
        {
          "ios": {
            "useFrameworks": "static"
          }
        }
      ]
    ],
    "ios": {
      "associatedDomains": [
        "applinks:myapp.redirectly.app",
        "webcredentials:myapp.redirectly.app"
      ]
    },
    "android": {
      "intentFilters": [
        {
          "action": "VIEW",
          "autoVerify": true,
          "data": [
            {
              "scheme": "https",
              "host": "*.redirectly.app"
            }
          ],
          "category": ["BROWSABLE", "DEFAULT"]
        }
      ]
    }
  }
}

Replace myapp with your Redirectly subdomain.

EAS Build for Production

For production Expo apps, use EAS Build to build your app with the correct certificates and entitlements:

Build Command

eas build --platform ios --profile production
eas build --platform android --profile production

Expo Go Limitations

Expo Go (the preview app) does not support custom URL schemes or deep linking from your subdomain. For testing deep links in development, use a custom dev client or EAS Build.

Migration

Migration from Firebase Dynamic Links

If you're currently using Firebase Dynamic Links (FDL), Redirectly offers a modern, simpler alternative with comparable functionality. The migration involves updating your SDK initialization and link handling code.

API Comparison

Firebase vs Redirectly

// Firebase Dynamic Links
const link = await dynamicLinks().getInitialLink();
if (link) {
  handleDeepLink(link.url);
}

dynamicLinks().onLink((link) => {
  handleDeepLink(link.url);
});

// Redirectly
const redirectly = Redirectly.getInstance();
await redirectly.initialize({ apiKey: 'your-key' });

redirectly.onAppInstalled((installData) => {
  if (installData.deferred?.url) {
    handleDeepLink(installData.deferred.url);
  }
});

For a detailed step-by-step migration guide with code examples and best practices, see our Firebase Dynamic Links alternative page. Also check out the deep linking guide and deferred deep linking concepts.

FAQ

Frequently Asked Questions

What is deferred deep linking?

Deferred deep linking allows links to work even if the app is not installed. When a user clicks a Redirectly link and doesn't have the app, they're directed to the app store. After installing, the app opens and the SDK retrieves the original link data, routing the user to the correct screen. This is critical for acquisition campaigns.

Does Redirectly work with Expo?

Yes, Redirectly works with both Expo and bare React Native. For Expo, configure deep linking in app.json and use EAS Build for production. The SDK is pure TypeScript and works out of the box.

What is install attribution?

Install attribution matches app installs to specific link clicks or campaigns. When a user installs the app after clicking a Redirectly link, the onAppInstalled callback provides details about which campaign, source, or medium drove the install. You can use this data for analytics and campaign optimization.

Do I need to write native code?

For bare React Native, you need to configure iOS and Android native deep link settings (entitlements, AndroidManifest.xml, AppDelegate). However, the Redirectly SDK itself is pure TypeScript with no native module code. For Expo, you configure deep linking in app.json.

How do I test deep links in the simulator/emulator?

For iOS Simulator, use xcrun simctl openurl. For Android Emulator, use adb shell am start. See the Testing & Debugging section for detailed commands and examples.

Can I migrate from Firebase Dynamic Links?

Yes. Redirectly provides similar functionality with a comparable API. See our Firebase Dynamic Links alternative page for step-by-step migration instructions and code comparisons.

Have questions? Check out the React Native SDK docs or explore related guides:

Get started with React Native

Sign up for a free API key and subdomain. Follow the React Native SDK docs and this guide to integrate deep linking.