SDK Usage
Initialize provider in app root
import { DetourProvider, type Config } from '@swmansion/react-native-detour';
const config: Config = {
apiKey: process.env.EXPO_PUBLIC_DETOUR_API_KEY!,
appID: process.env.EXPO_PUBLIC_DETOUR_APP_ID!,
shouldUseClipboard: true,
// linkProcessingMode: 'all' // default
};
export default function RootLayout() {
return (
<DetourProvider config={config}>
<RootNavigator />
</DetourProvider>
);
}
Use exactly one
DetourProviderin your app tree.On Expo Web,
DetourProvideris disabled: it returns a no-op context (isLinkProcessed: true,link: null) and does not process links.
If you want complete, runnable integrations for each navigation style, go to Examples.
Handle resolved links with useDetourContext()
import { useDetourContext } from '@swmansion/react-native-detour';
import { useEffect } from 'react';
import { useRouter } from 'expo-router';
export function RootNavigator() {
const { isLinkProcessed, link, clearLink } = useDetourContext();
const router = useRouter();
useEffect(() => {
if (!isLinkProcessed || !link) return;
router.replace({
pathname: link.pathname,
params: link.params,
});
clearLink();
}, [clearLink, isLinkProcessed, link, router]);
if (!isLinkProcessed) {
return null;
}
return <AppStack />;
}
link structure
useDetourContext() returns a link object (or null):
link.route: full in-app route with query string (e.g./details?utm=1)link.pathname: path without query (e.g./details)link.params: parsed query params recordlink.url: original URL or raw valuelink.type:'deferred' | 'verified' | 'scheme'
Use clearLink() after handling navigation to avoid repeated redirects.
Choose linkProcessingMode
Config.linkProcessingMode controls which link sources are handled by the SDK:
| Value | Deferred | Universal / App | Custom scheme |
|---|---|---|---|
'all' (default) | Yes | Yes | Yes |
'web-only' | Yes | Yes | No |
'deferred-only' | Yes | No | No |
Use 'deferred-only' when Expo Router +native-intent.tsx already handles runtime and initial URLs.
Expo Router integration
If you use Expo Router, we strongly recommend using our dedicated createDetourNativeIntentHandler API in a +native-intent.tsx file. This intercepts URLs at the router level before any screen renders, avoiding flash-of-wrong-screen issues and providing the smoothest deep-linking experience.
Expo Router's +native-intent.tsx file lets you intercept and transform incoming system URLs before the router processes them. The Detour SDK provides createDetourNativeIntentHandler — a factory that creates a handler pre-configured for Detour link resolution.
Basic setup
Create a +native-intent.tsx file in your app directory (e.g. src/app/+native-intent.tsx):
import { createDetourNativeIntentHandler } from '@swmansion/react-native-detour/expo-router';
export const redirectSystemPath = createDetourNativeIntentHandler();
This uses intercept mode: any URL matching a Detour host is caught and the user is routed to the home screen (default fallbackPath). Deferred links are still handled separately by DetourProvider.
Resolve mode
To resolve Detour short links to their full destination URLs at the router level, pass a config object:
import { createDetourNativeIntentHandler } from '@swmansion/react-native-detour/expo-router';
export const redirectSystemPath = createDetourNativeIntentHandler({
config: {
apiKey: process.env.EXPO_PUBLIC_DETOUR_API_KEY!,
appID: process.env.EXPO_PUBLIC_DETOUR_APP_ID!,
},
});
In resolve mode, when a user taps a Detour short link (e.g. https://abc123.godetour.link/xyz), the handler resolves it to the full destination URL and routes directly to the correct screen — all before the first render.
See API Reference — Expo Router helper for the full list of options (custom hosts, fallback path, timeout, route mapping, etc.).
Configure DetourProvider for native intent
When using +native-intent.tsx to handle Universal Links and App Links, configure the provider to only process deferred links:
const config: Config = {
apiKey: process.env.EXPO_PUBLIC_DETOUR_API_KEY!,
appID: process.env.EXPO_PUBLIC_DETOUR_APP_ID!,
linkProcessingMode: 'deferred-only',
};
This avoids double-processing: +native-intent.tsx handles runtime and initial URLs, while DetourProvider handles deferred deep links only.
Advanced examples
The SDK repository includes examples for common native intent scenarios:
-
expo-router-native-intent— Full resolve mode setup with a custommapToRoutefunction and scheme URL handling. Use this as a starting point when you need to transform resolved URLs before routing (e.g. stripping path prefixes) or when you want to handle custom scheme links alongside Detour links. -
expo-router-advanced— Demonstrates how to coexist Detour's native intent handler with your own custom routing logic. The handler first delegates to Detour; if the URL isn't a Detour link, it falls through to custom rules (e.g. routing third-party deep links to a dedicated screen). Use this when your app handles links from multiple sources beyond Detour.
For all runnable examples, see Examples.
Alternative: catch-all route without +native-intent.tsx
If you prefer not to use +native-intent.tsx, you can handle all incoming links through DetourProvider with linkProcessingMode: 'all' (the default) and implement a catch-all route in your navigation stack.
In this approach, DetourProvider processes all link types (deferred, Universal/App Links, and custom scheme). You consume the resolved link via useDetourContext() in a top-level component and navigate programmatically:
const { isLinkProcessed, link, clearLink } = useDetourContext();
useEffect(() => {
if (!isLinkProcessed || !link) return;
router.replace({ pathname: link.pathname, params: link.params });
clearLink();
}, [isLinkProcessed, link]);
This was the primary integration pattern before the SDK introduced native intent support (pre-1.x). It still works but the +native-intent.tsx approach provides better UX by resolving links before the first screen renders.
Analytics module
DetourAnalytics emits events that are sent by the active DetourProvider.
import {
DetourAnalytics,
DetourEventNames,
} from '@swmansion/react-native-detour';
DetourAnalytics.logEvent(DetourEventNames.Purchase, {
item: 'subscription_premium',
currency: 'USD',
});
DetourAnalytics.logRetention('home_screen_opened');