This is a guest post from Bartek Krasoń - a software engineer from Software Mansion who is currently working on Detour.
…
If you're building a mobile app with Expo, there's a good chance you're using Expo Router to keep your navigation logic predictable and scalable.
You've likely already set up standard deep linking (Universal Links for iOS, App Links for Android) and everything works perfectly for your existing users. But what happens when a new user clicks a link to a private invite or a specific product, and they don't have your app yet?
What’s happening (and why)
Standard deep links (Universal Links on iOS, App Links on Android) work great when the app is already installed. The link opens the app, Expo Router sends them to the right screen. Simple.
But the moment a new user passes through the App Store or Google Play, that URL context vanishes. Because iOS and Android don't preserve link data across the store boundary, your app launches completely blind to the original intent that drove the install.
This friction is known as the Installation Gap. The fix? Deferred deep linking. The goal is to capture the user's original intent – the exact URL, path, and parameters they tapped, persist it through the installation process, and replay it the moment the app launches for the very first time.
The existing option
Some platforms have had this solved for years, but they are marketing attribution tools first – for them, deep linking is just a side feature. They hand you a raw URL string in a callback, leaving you to bridge it to your navigation yourself.
In an Expo Router app, this means writing fragile glue code to manage lifecycle timing, duplicate navigation events, and race conditions with your auth gates. Because the SDK operates in a completely separate world from your router, you’re stuck building plumbing that shouldn’t have to exist in the first place.
How Detour approaches this
We built Detour at Software Mansion to solve this problem from the navigation side. Our team has been contributing to the Expo ecosystem since 2017 (Reanimated, Gesture Handler, parts of EAS), and we wanted a deferred deep linking tool that integrates with the router, not around it.
The core idea: Detour can resolve links before the first screen renders, using Expo Router's own extension points. The router knows about the intent from the start, so there's no post-mount useEffect race and no manual URL parsing.
Here's what that looks like in practice.
Intercept the link before the first render
Expo Router exposes a +native-intent.tsx file that lets you intercept and transform incoming URLs before routing begins. Detour hooks into this directly:
// app/+native-intent.tsximport { createDetourNativeIntentHandler } from "@swmansion/react-native-detour/expo-router";export const redirectSystemPath = createDetourNativeIntentHandler({fallbackPath: "/",hosts: [/\.godetour\.link$/i],});
This acts as pre-routing middleware. When the app launches, Detour checks whether this is a deferred deep link, resolves the original URL, and feeds it to the router before any screen mounts.
Handle the auth gate
There's a common catch, though: the auth gate. A user taps an invite link, installs the app, and opens it. Detour resolves the original URL. But the user isn't logged in yet, so your auth guard redirects them to /login. The resolved link gets swallowed.
DetourProvider solves this by holding the link intent in memory until your app signals the user is ready:
// app/_layout.tsximport { DetourProvider, useDetourContext } from "@swmansion/react-native-detour";export default function RootLayout() {return (<DetourProvider config={{ appID: "YOUR_APP_ID", apiKey: "YOUR_API_KEY" }}><AuthProtectedStack /></DetourProvider>);}function AuthProtectedStack() {const { link, clearLink, isLinkProcessed } = useDetourContext();const { isSignedIn } = useAuth(); // Your logicconst router = useRouter();useEffect(() => {if (isLinkProcessed && link && isSignedIn) {clearLink(); // Ensure the link only fires oncerouter.replace(link.route);}}, [link, isLinkProcessed, isSignedIn]);return <Stack />;}
The link stays in memory until isSignedIn becomes true. Once the user logs in (or if they were already logged in), the navigation fires exactly once and clears itself, so there are no duplicate triggers or races with the auth redirect.
How matching works under the hood
Deferred deep linking needs to answer one question: "Is this the same person who clicked that link n seconds ago in the browser?" – the answer depends on the platform.
Android: deterministic matching. Detour passes a unique click_id to the Google Play Store via the Install Referrer API. When the app launches for the first time, the SDK retrieves that exact click_id. This is a 1:1 match with 100% accuracy, because Android provides a direct data channel between the referral click and the first app launch.
iOS: probabilistic matching. Apple treats the App Store as a privacy boundary. There's no equivalent of the Install Referrer API. Instead, Detour captures a snapshot of non-identifying signals during the browser click and matches them to a second snapshot taken when the app opens.
Why this matters at the router level
By handling links through +native-intent.tsx, Detour prevents common glitches where a single link triggers multiple navigations or conflicts with the initial URL.
With a typical third-party SDK, the incoming link lands in a detached listener that operates separately from the navigation structure. You're left to manage the hand-off between a browser click and the app's internal state – dealing with auth gate conflicts, deep nesting issues, and redundant triggers. The SDK and the router live in separate worlds.
Since the router is the exact place where the user journey transitions from the web to the app, Expo Router is an important piece of the puzzle – our goal is to keep Detour in sync as the platform evolves, so the hand-off feels like a deliberate part of the UX rather than a technical side effect.
The road ahead
Detour 1.0 established the core essentials, including custom domains and SDKs for React Native, Flutter, and native platforms. Now, we’re looking at what comes next. Moving forward, we want to expand the platform to cover more parts of the user journey, while keeping everything developer-first and affordable.
We're actively developing Detour and would love feedback from the Expo community. If you run into issues or have feature requests, open an issue on GitHub or reach out on our Discord!


