Installation
Detour provides a React Native SDK (@swmansion/react-native-detour) compatible with Expo and bare React Native projects.
A Detour app in the Dashboard is required before the SDK can resolve links. See Getting Started.
Install package
Section titled “Install package”npm install @swmansion/react-native-detouryarn add @swmansion/react-native-detourpnpm add @swmansion/react-native-detourbun add @swmansion/react-native-detourPeer dependencies (Expo)
Section titled “Peer dependencies (Expo)”For Expo projects (including prebuild), install peer dependencies with Expo CLI so versions are matched to your Expo SDK:
npx expo install expo-application expo-clipboard expo-constants expo-localization @react-native-async-storage/async-storageBare React Native projects
Section titled “Bare React Native projects”If you are not using Expo CLI, install peer dependencies with your package manager and pin versions compatible with your React Native/Expo Modules setup.
npm install expo-application expo-clipboard expo-constants expo-localization @react-native-async-storage/async-storageyarn add expo-application expo-clipboard expo-constants expo-localization @react-native-async-storage/async-storagepnpm add expo-application expo-clipboard expo-constants expo-localization @react-native-async-storage/async-storagebun add expo-application expo-clipboard expo-constants expo-localization @react-native-async-storage/async-storageExpo Modules setup (React Native CLI)
Section titled “Expo Modules setup (React Native CLI)”Four of the peer dependencies above are Expo Modules:
expo-applicationexpo-clipboardexpo-constantsexpo-localization
They ship native code that has to be autolinked into your android/ and ios/ projects. An Expo project does this for you. A bare React Native CLI project needs Expo Modules added once:
npx install-expo-modules@latestThis sets up Expo Modules autolinking (and on iOS raises the deployment target to the Expo Modules minimum).
install-expo-modules also switches the native JS bundling step to Expo CLI (export:embed). That requires metro.config.js to extend expo/metro-config, which is a superset of @react-native/metro-config and safe in bare projects. The tool usually migrates the file for you, but verify it, especially if you had customized it:
const { getDefaultConfig } = require("expo/metro-config");const { mergeConfig } = require("@react-native/metro-config");
module.exports = mergeConfig(getDefaultConfig(__dirname), {});Then align the Expo package versions to your setup:
npx expo install --fixDevice info provider
Section titled “Device info provider”Deferred-link matching builds a probabilistic fingerprint of the device. The SDK reads three values for it:
- device model
- manufacturer
- OS version
Those values come from a device info library. Install one of the two supported ones. Both are optional peer dependencies, and the SDK uses whichever it finds:
npx expo install expo-devicenpm install react-native-device-infoNative link configuration
Section titled “Native link configuration”Installing the package does not register incoming links with iOS or Android.
Copy the generated ios.associatedDomains and android.intentFilters values
from App configuration in the Dashboard into app.json, app.config.js, or
app.config.ts:
{ "expo": { "ios": { "associatedDomains": ["applinks:<your-host>"] }, "android": { "intentFilters": [ { "action": "VIEW", "autoVerify": true, "data": [ { "scheme": "https", "host": "<your-host>", "pathPrefix": "/<app-hash>" } ], "category": ["BROWSABLE", "DEFAULT"] } ] } }}Use the exact Dashboard output: it also contains any required legacy short-link
prefixes. New short links reuse /<app-hash> and do not require another native
build.
If your app is served from a custom domain, the Dashboard generates both values for that domain. Assign the domain before you copy them. Changing the host an app is assigned to later needs a new build. Do not add hosts it did not generate: on Android 11 and lower that breaks App Link verification for every host at once, see Verification.
The same host belongs in the handler’s
hosts list, which is separate
from the native configuration.
Custom schemes are optional and are not generated by the Dashboard. If you use
links such as myapp://product/42, add the scheme separately:
{ "expo": { "scheme": "myapp" }}These are build-time settings. After changing the app config, create a new
native build. In a Continuous Native Generation workflow, regenerate the native
projects with npx expo prebuild --clean, then run npx expo run:ios or
npx expo run:android. Alternatively, create a new EAS development build.
Reloading Metro or publishing an OTA update does not apply native configuration
changes.
Bare React Native
Section titled “Bare React Native”iOS Universal Links
Section titled “iOS Universal Links”In Xcode, open your app target, select Signing & Capabilities, add the
Associated Domains capability, and enter applinks:<your-host>. The
resulting entitlements entry is:
<key>com.apple.developer.associated-domains</key><array> <string>applinks:<your-host></string></array>Android App Links
Section titled “Android App Links”Paste the generated Dashboard filter into the MainActivity declaration in
AndroidManifest.xml:
<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" /> <data android:host="<your-host>" /> <data android:pathPrefix="/<app-hash>" /> <!-- The Dashboard adds any required legacy short-link prefixes. --> </intent-filter></activity>Use the exact host and paths generated for your app.
Custom-scheme links (optional)
Section titled “Custom-scheme links (optional)”The Dashboard does not generate custom-scheme registration. If you use scheme
links, add CFBundleURLTypes to the iOS Info.plist:
<key>CFBundleURLTypes</key><array> <dict> <key>CFBundleURLSchemes</key> <array> <string>myapp</string> </array> </dict></array>Add a separate filter to the Android MainActivity:
<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>iOS callback forwarding
Section titled “iOS callback forwarding”DetourProvider reads cold-start and runtime URLs through React Native’s
Linking API. Verify that your AppDelegate forwards Universal Link and
custom-scheme callbacks to RCTLinkingManager.
The snippets below are for an AppDelegate that does not already provide this
forwarding. If its superclass or another integration handles links, preserve
that behavior and merge the handlers instead of adding duplicate forwarding or
replacing the existing implementation.
import React
func application( _ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { return RCTLinkingManager.application( application, continue: userActivity, restorationHandler: restorationHandler )}
func application( _ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { return RCTLinkingManager.application(app, open: url, options: options)}#import <React/RCTLinkingManager.h>
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler{ return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{ return [RCTLinkingManager application:application openURL:url options:options];}Android callback delivery
Section titled “Android callback delivery”With the standard MainActivity : ReactActivity, no Detour-specific callback is
required. React Native reads the cold-start VIEW intent from the activity and
forwards later onNewIntent calls to Linking.
Set android:launchMode="singleTask" when links should be delivered to the
existing MainActivity instance. React Native then emits the runtime url
event consumed by DetourProvider. If you use a custom activity that does not
inherit React Native’s default intent handling, you must forward its intents to
React Native yourself.
Credentials
Section titled “Credentials”Both values come from API configuration in the Dashboard: the appID and the publishable apiKey. See Getting Started.
import { type Config } from '@swmansion/react-native-detour';
export const detourConfig: Config = { apiKey: process.env.EXPO_PUBLIC_DETOUR_API_KEY!, appID: process.env.EXPO_PUBLIC_DETOUR_APP_ID!, shouldUseClipboard: true,};Keep apiKey and appID in environment variables or another secure configuration layer.
Next steps
Section titled “Next steps”- Implement the provider and link handling in Usage.
- Pick a runnable app pattern in Examples.
- Verify the native link configuration in Testing.
- Fill in the store declarations from Privacy before you submit a build.
- If upgrading existing code, follow Migration to v2 and start from From 0.x or From 1.0.1.