Skip to main content

Generate React Native deep linking config

Enter your domain and the screens that should open from a link. We write the React Navigation linking config, the iOS entitlement and AppDelegate hooks, the Android intent filters, or the Expo app.json.

Your app

Everything runs in your browser. Nothing you enter is sent to a server.

Expo apps set links in app.json. Bare apps edit the iOS and Android projects.

Dynamic passes a linking prop to NavigationContainer. Static puts a linking entry on each screen.

One host per line, without https://. These host your Universal Links and App Links.

One per line: a URL path, then the screen it opens. Write Shop.Cart for a screen inside a nested navigator.

Lowercase, without ://. Leave it empty to use only https links.

In Xcode under Signing & Capabilities. Used to prefill the AASA generator.

Ten characters. Usually your Team ID, in your Apple Developer account under Membership details.

The applicationId in android/app/build.gradle. Used to prefill the assetlinks generator.

Your config

Each file shows where it goes in your project.

linking.ts

src/navigation/linking.ts (Pass it to NavigationContainer as the linking prop.)

import type { LinkingOptions } from '@react-navigation/native';

export const linking: LinkingOptions<ReactNavigation.RootParamList> = {
  prefixes: [
    'https://links.example.com',
    'myapp://',
  ],
  config: {
    screens: {
      ProductDetails: 'product/:id',
      Cart: 'cart',
    },
  },
};

// <NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>

YourApp.entitlements

ios/YourApp/YourApp.entitlements (Or add the Associated Domains capability in Xcode under Signing & Capabilities.)

<?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:links.example.com</string>
  </array>
</dict>
</plist>

AppDelegate-linking.swift

ios/YourApp/AppDelegate.swift (Hands links to React Native's Linking module.)

// Add both methods inside your AppDelegate class.
// The React Native 0.77+ template already has `import React`.

func application(
  _ app: UIApplication,
  open url: URL,
  options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
  return RCTLinkingManager.application(app, open: url, options: options)
}

func application(
  _ application: UIApplication,
  continue userActivity: NSUserActivity,
  restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
  return RCTLinkingManager.application(
    application,
    continue: userActivity,
    restorationHandler: restorationHandler
  )
}

Info-url-types.xml

ios/YourApp/Info.plist (Add inside the top-level dict.)

  <key>CFBundleURLTypes</key>
  <array>
    <dict>
      <key>CFBundleURLSchemes</key>
      <array>
        <string>myapp</string>
      </array>
    </dict>
  </array>

AndroidManifest-intent-filters.xml

android/app/src/main/AndroidManifest.xml (Add inside <activity android:name=".MainActivity">, which keeps android:launchMode="singleTask".)

  <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" />
    <data android:host="links.example.com" />
    <data android:pathPrefix="/product/" />
    <data android:path="/cart" />
  </intent-filter>
  <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>

Now generate the files your domain serves

The app config opens your app only after links.example.com serves an AASA file and an assetlinks.json file that name it. Both generators open with this app filled in.

Already serving the files?

The deep link checker fetches both files from links.example.com and reports what a device would read.

How it works

1. Pick your app type
Bare React Native edits the iOS and Android projects. Expo sets the same values in app.json.
2. Enter your link domains
Each host becomes a React Navigation prefix, an Associated Domains entry and an Android host.
3. Map paths to screens
Write /product/:id -> ProductDetails for each screen. Use Shop.Cart to nest a screen in a navigator.
4. Copy each file to its path
The label above every file says where it goes. Add the domain files next, then test on a device.

Reference

The fields

Only the domain and the routes are required. The identifiers fill in app.json and prefill the domain-file generators.

FieldWhat to enter and where to find it
App typeBare React Native, or Expo. Expo writes app.json fields and needs a new native build after a change.
React Navigation 7 APIDynamic passes a linking object to NavigationContainer. Static puts a linking entry on each screen of createXNavigator.
Link domainsThe hosts that serve your links, one per line, such as links.example.com. No https:// and no path.
RoutesOne /path -> ScreenName per line. :id is a parameter, :id? is optional, and /* matches anything left. Use a dot to nest a screen, and put its path under the parent's path.
Custom URL schemeOptional. The part before ://, in lowercase. It is not verified, so it suits testing and places that reject https.
iOS bundle IDOptional. In Xcode under Signing & Capabilities, or PRODUCT_BUNDLE_IDENTIFIER in the project.
App ID prefixOptional. Ten characters, usually your Team ID under Membership details in your Apple Developer account.
Android package nameOptional. The applicationId in android/app/build.gradle.

Versions these snippets target

ToolVersion
React Native0.77 and later, with the Swift AppDelegate. Earlier Objective-C projects call the same two RCTLinkingManager methods.
React Navigation7.x, both the dynamic linking prop and the static API.
ExpoSDK 50 and later. Expo Router derives its routes from the files under app/, so the generator lists the files.
iOSUniversal Links through the Associated Domains capability.
AndroidApp Links with android:autoVerify, which Android 6.0 and later check against assetlinks.json.

When a link does not open the right screen

What you seeLikely cause and fix
iOS opens Safari, not the appThe Associated Domains entry is missing, the host differs from the one in your AASA file, or the App ID in that file is wrong. Check the domain with the deep link checker, then reinstall the app because iOS reads the file at install.
Android shows a chooserVerification failed. assetlinks.json is missing, is served through a redirect, or lists a different package or SHA-256 certificate fingerprint.
The app opens on the wrong screenReact Navigation ranks patterns by how specific they are, so the order in the config does not decide. Look for a broad pattern such as /* that also fits the link, and check that each screen name matches your navigator exactly.
Cold start works, a warm link does nothingOn iOS, the continue userActivity method is missing from AppDelegate. On Android, MainActivity needs android:launchMode="singleTask".
The custom scheme works, https does notThe scheme needs no domain file. https links need the AASA file, assetlinks.json and the native settings together.
Expo changes have no effectassociatedDomains and intentFilters are native settings. Make a development build or run expo prebuild again. Expo Go cannot verify your domain.

Need deferred deep links and install attribution?

The config on this page opens a screen when the app is installed and a person taps a link. It cannot help the person who taps a link before installing: they go to the store, then open the app with no link in hand.

Deferred deep links, which carry that link through the install, and install attribution need an SDK. The WarpLink React Native SDK delivers the original link on first open and records which link led to the install, next to the config you built here.

What a React Native app needs for links

A link opens a screen through four pieces. React Navigation maps URL paths to screens in its linking config. iOS needs the Associated Domains entitlement for your host and two AppDelegate methods that hand the URL to React Native. Android needs an intent filter with android:autoVerify so the system opens your app without asking. Your domain serves an apple-app-site-association file and an assetlinks.json file that name the app.

The linking config handles a cold start and a link received while the app runs. This page writes the first three pieces. The AASA and assetlinks generators write the files your domain serves, and the deep link checker reads them back from your live domain. Test on a signed build after each change, because simulators and emulators do not verify domains the way devices do.

Frequently asked questions

How do I map nested screens?

Write the parent and the screen with a dot, such as Shop.Product, and the generator nests the screens object the way your navigators nest. The nested path is written relative to its parent, so /shop/:id under Shop becomes :id. The static API lists root screens only, so nest by declaring the linking entry inside each navigator.

Do I need a custom scheme?

Not for links on your own domain. Universal Links and App Links use https URLs. A custom scheme is useful for local testing and for places that do not accept https, and it needs no domain file.

How do I support two domains?

Put one host on each line. Both go into the React Navigation prefixes, the Associated Domains entitlement and the Android intent filter. Each host serves its own AASA and assetlinks.json file.

Why does the app open the wrong screen?

React Navigation ranks patterns by how specific they are, so listing a specific path first changes nothing. Look for a broad pattern such as /* that also fits the link, a nested screen that repeats its parent's path, and a screen name that differs from the one in your navigator.

Should I use the dynamic or the static API?

Use whichever matches how you build navigators. The dynamic API is a linking object with a config.screens tree. The static API, from React Navigation 7, declares the path on each screen and passes only the prefixes to the Navigation component.

Does this work with Expo Router?

Expo Router builds its linking from the files under app/, so it needs no linking config. Choose Expo and the generator writes app.json and lists the file for each path, such as app/product/[id].tsx.

What must I test on a device?

Open a link from a cold start and from the background on both platforms, and tap a link from Notes or Messages. Simulators and emulators do not replace a signed build for verification.

We handle the link layer for you

Add the WarpLink React Native SDK for deep links, deferred deep links and install attribution on top of this config.

Start buildingFree to start. No sales call.