Skip to content
Dashboard

Usage

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>
);
}

If you want complete, runnable integrations for each navigation style, go to Examples.

Section titled “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 />;
}

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 record.

link.url

Original URL or raw value.

link.type
Type 'deferred' | 'verified' | 'scheme'

Use clearLink() after handling navigation to avoid repeated redirects.

Config.linkProcessingMode controls which link sources are handled by the SDK:

ValueDeferredUniversal / AppCustom scheme
'all' (default)YesYesYes
'web-only'YesYesNo
'deferred-only'YesNoNo

Use 'deferred-only' when Expo Router +native-intent.tsx already handles runtime and initial URLs.

When a Universal Link or App Link is processed, the SDK verifies the click against your plan’s click limit before delivering the resolved link. While your app is within the limit, links resolve normally. When the limit is exceeded, the link is not delivered:

  • With DetourProvider, link stays null and useDetourContext() performs no navigation.
  • With createDetourNativeIntentHandler in resolve mode, the handler returns fallbackPath.

In both cases the SDK logs a 🔗[Detour:CLICK_LIMIT_ERROR] message with the limit details (clicksInPeriod, effectiveLimit).

This dedicated 🔗[Detour:CLICK_LIMIT_ERROR] flow applies only to Universal/App links. Deferred-link matching and short-link resolution are subject to the same plan limits on the backend, but the SDK does not surface a dedicated limit error for them. When the limit is exceeded the affected link fails to resolve (it returns null) instead of logging a click-limit error. Custom scheme links are handled locally and are not limit-checked.

For the actual per-plan click limits, pay-as-you-go overage, and how to raise them, see Billing & Payments.

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.

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();

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 Expo Router helper for the full list of options (custom hosts, fallback path, timeout, route mapping, etc.).

Configure DetourProvider for native intent

Section titled “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.

The SDK repository includes examples for common native intent scenarios:

  • expo-router-native-intent — Full resolve mode setup with a custom mapToRoute function 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 Detour inside an auth- and onboarding-gated flow. Deferred and runtime links are preserved through sign-in and one-time onboarding before navigating to their destination. Use this when your app requires the user to be signed in (and optionally onboarded) before a deep link should take effect.

For all runnable examples, see Examples.

Alternative: catch-all route without +native-intent.tsx

Section titled “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.

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");

Besides the events you log manually, DetourProvider reports some events on its own, with no wiring needed:

  • app_open — a retention event logged once per session when the provider first mounts (cold start).

Universal Link and App Link opens are no longer emitted as a client event. They are recorded by the backend through a dedicated click endpoint (see Click limits).