Skip to content

Latest commit

 

History

History
221 lines (140 loc) · 12.9 KB

File metadata and controls

221 lines (140 loc) · 12.9 KB

Detour React Navigation Example (Advanced)

An auth-gated React Navigation app with @swmansion/react-native-detour. A deep link can arrive at any point in the SignIn → Onboarding → Tabs flow; React Navigation remembers it and replays it once the target screen becomes reachable.


Sign-in screen with a Detour link pending First-launch onboarding screen Details screen reached after the pending link is replayed

Auth-gated flow. A Detour link arrives while signed out, so the app waits on the sign-in screen (left) → after signing in, a first-time user goes through onboarding (center) → the pending link is replayed and the app lands on Details (right).

  • Screen flow: SignInOnboarding (once per install) → Tabs (Home, Explore, Settings) + Details.
  • Detour feeds URLs via Detour.getInitialURL() and Detour.addEventListener("url", ...).
  • UNSTABLE_routeNamesChangeBehavior="lastUnhandled" makes React Navigation remember an unresolvable deep link and replay it once the target screen becomes renderable.
  • All link types handled: Universal/App links, custom scheme, and deferred.

Related examples:

Auth-gated deferred link behavior

How a link survives sign-in and onboarding
  • If a deferred link arrives and the user is not signed in, the splash hides and SignIn is shown. React Navigation parses the URL, finds Details is not currently rendered, and marks the action as the last unhandled one.
  • After sign-in, the rendered screen set changes. If onboarding has not been completed yet, Onboarding is shown — Details is still not rendered, so the pending link stays remembered.
  • After onboarding, Details becomes part of the rendered stack. React Navigation retries the unhandled action and navigates to Details (or falls through to NotFound).

Note: UNSTABLE_routeNamesChangeBehavior="lastUnhandled" is not deep-link-specific. It also captures other unhandled navigation actions — for example a manual navigation.navigate(...) call or an initialState pointing at a screen that isn't currently rendered — and replays them once that screen becomes part of the navigator. See the React Navigation docs for the full behavior.

Expected dev-only warning

When the link arrives while the target screen isn't rendered yet (e.g. on SignIn), React Navigation logs a development-only warning.

This is the dispatch attempt against the current (signed-out) navigator state. UNSTABLE_routeNamesChangeBehavior="lastUnhandled" then stashes the action and replays it once Details is part of the rendered stack. The message is stripped in production builds.

Reference docs:

React Navigation deep linking

React Navigation auth flow (see UNSTABLE_routeNamesChangeBehavior)

Test flow

Deferred link

  1. Follow the Deferred deep link setup to register a pre-install click, then install and launch the app signed out. The deferred match runs only on this fresh first launch.
  2. You land on SignIn — Detour has matched the click and is holding the link, so a Link pending banner appears.
  3. Tap Sign in, then tap Get Started on the onboarding screen (shown on the first launch).
  4. React Navigation replays the held link once Details becomes reachable, so the app lands on Details with the forwarded params visible.
  5. Go back — the same link does not trigger again.

Universal / App link

Same flow as above but don't need to reinstall — the link arrives at runtime. While signed out (tap Logout to get there), trigger a Detour link to /details (see Triggering links). On later launches the onboarding step is skipped.

Custom scheme

Same runtime flow as the Universal / App link, just opened through the app's custom scheme. Trigger the custom-scheme URL (see Triggering links) — it's held through the auth gate and replayed to Details the same way.

Set up Detour

You need a Detour account to register this app and generate its credentials. Sign up and open the Detour Dashboard. If you run into issues during setup, the Dashboard Walkthrough covers each step in detail.

1. Register the app

Create an organization and add a new app. Detour assigns it a base link URL of the form https://<your-org>.godetour.link/<your-app-hash> visible in Link settings section.

In Link settings, the dashboard asks for a fallback Redirect URL to mark setup as complete. It only controls where web traffic lands — it has no effect on the deferred or Universal/App link flows these examples test, so it can be left empty or filled with a placeholder URL for local development. For production, see Full app configuration.

Dashboard › Apps

Detour Dashboard organization creator Detour Dashboard app creator

Detour Dashboard app link details


Dashboard. Create an organization (top-left), create a new app (top-right), and use the generated link (marked with red) in Link settings (bottom).

2. Configure the platforms

Open App configuration and fill in the platform details:

  • iOS: set Bundle ID to detourreactnative.reactnavigationadvanced and provide Team ID and App Store ID. The Team ID must be your real Apple Developer Team ID — the one the build is signed with (DEVELOPMENT_TEAM in Xcode › Signing & Capabilities, also shown under Apple Developer › Membership). A placeholder or mismatched Team ID makes the Universal link open in Safari instead of the app. The App Store ID can stay a placeholder for local development.
  • Android: set package name to detourreactnative.reactnavigationadvanced and add a SHA-256 certificate fingerprint. The fingerprint must match the keystore that signs the build — a wrong value makes Android open the App link in the browser instead of the app. For local development (npx expo run:android), use the local debug keystore fingerprint. See Testing Android App Links for more info.

The dashboard generates the associatedDomains and intent-filter snippets to paste into app.json (below).

For a production integration, fill all fields with real values. See App configuration for full guidance.

Dashboard › App configuration

Detour Dashboard App configuration


Dashboard › App configuration. iOS and Android filled configurations with generated integration code snippets ready to copy.

3. Copy your credentials

Open API configuration and copy your appID and publishable apiKey into this example's .env.

Dashboard › API configuration

Detour Dashboard API configuration


Dashboard › API configuration. The API configuration panel with appID and the publishable apiKey ready to copy.

4. (Optional) Tune deferred-link matching

Because this example exercises the deferred case through a multi-step gate, you may want to review the Matching settings (threshold and time window) that control how a pre-install click is paired with the first launch.

Matching

Detour Dashboard Matching configuration


Dashboard › Link Settings › Matching. The matching panel showing confidence threshold and time-window controls alongside match statistics.

Configuring app.json

Replace the placeholders in app.json with the values from the dashboard's App configuration section:

  • <your-org> — your organization slug
  • <your-app-hash> — the path prefix assigned to your app
"ios": {
  // ...
  "associatedDomains": ["applinks:<your-org>.godetour.link"]
},
"android": {
  // ...
  "intentFilters": [{
    // ...
    "data": [{ "scheme": "https", "host": "<your-org>.godetour.link", "pathPrefix": "/<your-app-hash>" }]
  }]
}

Universal Links not opening the app? Add ?mode=developer to the associatedDomains entry: "applinks:<your-org>.godetour.link?mode=developer". This bypasses Apple's CDN and fetches the AASA file directly from your domain on every launch instead of relying on a potentially stale cached version. Requires Settings → Developer → Associated Domains Development to be enabled on the device and a development-signed build. Remove it before submitting to TestFlight or the App Store.

These same values go into the simulator commands in the next section.

Triggering links

Deferred deep link

Follow these steps to test the deferred flow:

  1. Uninstall the app or clear its data to start from a clean state.
  2. Open a Detour link in the device's mobile browser.
  3. Install and launch the app — the SDK resolves the link automatically.

Note: On Android, the install referrer is typically unavailable in development builds, so deferred matching falls back to probabilistic signals (IP, device fingerprint) only. See Limitations & Known Issues.

Alternatively on iOS, you can also copy the link to your clipboard before uninstalling — the SDK reads the clipboard on first launch (shouldUseClipboard: true), so you can skip the browser step. It simulates a link clicked before the app was installed.

Universal / App link

Open a Detour link directly from the terminal:

# iOS simulator
xcrun simctl openurl booted "https://<your-org>.godetour.link/<your-app-hash>/details"

# Android emulator
adb shell am start -a android.intent.action.VIEW -d "https://<your-org>.godetour.link/<your-app-hash>/details"

Alternatively, paste the link into Notes or Messages on the device and tap it — this uses the same OS routing path a real user would.

Custom scheme
# iOS simulator
npx uri-scheme open "detour-react-navigation-advanced://details" --ios

# Android emulator
npx uri-scheme open "detour-react-navigation-advanced://details" --android

For more cases and gotchas, see Testing & Troubleshooting.

Quick start

  • Install dependencies from the repo root: pnpm install
  • Configure this app in the Detour Dashboard using identifiers from app.json (for example ios.bundleIdentifier, android.package).
  • Use the values from the dashboard's API configuration section to fill .env and update app.json with the generated integration code.
  • Run prebuild for this example: pnpm prebuild
  • Start the example: pnpm start
  • Run on device/simulator: pnpm ios or pnpm android

See also