Skip to content

Commit 5c52474

Browse files
authored
Update readme (#66)
* docs: update readme * docs: update version in readme * docs: fix link
1 parent 8b70135 commit 5c52474

1 file changed

Lines changed: 163 additions & 71 deletions

File tree

README.md

Lines changed: 163 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,17 @@
66

77
# React Native Detour
88

9-
SDK for handling deferred links in React Native.
10-
11-
## Create an account
12-
13-
You need a Detour account to generate app credentials and configure your links.
14-
Sign up here: [https://godetour.dev/auth/signup](https://godetour.dev/auth/signup)
9+
React Native Detour is an SDK for handling deferred deep links in React Native. A deferred link works like a regular deep link, but survives the App Store or Play Store install — a user who clicks a link before having the app installed is redirected to the right screen on first launch. Detour also handles Universal/App links and custom scheme links in a single unified API.
1510

1611
## Quick links
1712

18-
- Documentation: [https://docs.swmansion.com/detour/docs/](https://docs.swmansion.com/detour/docs/)
19-
- Installation guide: [https://docs.swmansion.com/detour/docs/sdk/react-native/sdk-installation](https://docs.swmansion.com/detour/docs/sdk/react-native/sdk-installation)
20-
21-
## Other Detour SDKs
13+
- Documentation: [https://detour.swmansion.com/docs/](https://detour.swmansion.com/docs/)
14+
- Installation guide: [https://detour.swmansion.com/docs/sdk/react-native/sdk-installation](https://detour.swmansion.com/docs/sdk/react-native/sdk-installation)
2215

23-
Detour is also available for other app stacks:
16+
## Create an account
2417

25-
- Android SDK: [https://github.com/software-mansion-labs/android-detour](https://github.com/software-mansion-labs/android-detour)
26-
- iOS SDK: [https://github.com/software-mansion-labs/ios-detour](https://github.com/software-mansion-labs/ios-detour)
27-
- Flutter SDK: [https://github.com/software-mansion-labs/detour-flutter-plugin](https://github.com/software-mansion-labs/detour-flutter-plugin)
18+
You need a Detour account to generate app credentials and configure your links.
19+
Sign up here: [https://godetour.dev/auth/signup](https://godetour.dev/auth/signup)
2820

2921
## Installation
3022

@@ -48,19 +40,34 @@ npm install expo-device
4840
npm install react-native-device-info
4941
```
5042

51-
> You can override the default persistent storage (@react-native-async-storage/async-storage) by providing an alternative storage implementation. Pass your custom storage object via the configuration settings.
43+
> You can override the default persistent storage (`@react-native-async-storage/async-storage`) by providing an alternative storage implementation via the `storage` config option.
44+
>
45+
> For device info, install either `expo-device` or `react-native-device-info` — at least one is required. If your project already uses one of them, no extra installation is needed.
5246
5347
## Usage
5448

55-
### Initialize the provider
49+
Mount `DetourProvider` at the root of your app and configure it with your credentials. How you consume the resolved link depends on your navigation library.
50+
51+
> The SDK is a no-op on Expo Web — `DetourProvider` mounts but link processing is skipped and `isLinkProcessed` resolves immediately to `true`.
52+
53+
### Expo Router
54+
55+
Wrap your root layout with `DetourProvider`, then use the `useDetourContext` hook to read the resolved link and drive navigation. If your app uses Expo Router's `+native-intent.tsx` to handle Universal/App links, import `createDetourNativeIntentHandler` from `@swmansion/react-native-detour/expo-router` and set `linkProcessingMode: 'deferred-only'` — Detour will only handle deferred links and let the native intent handler take care of the rest. See [`examples/expo-router-native-intent`](./examples/expo-router-native-intent) for a working setup.
56+
57+
<details>
58+
<summary>Expo Router example</summary>
59+
60+
```tsx
61+
import { Stack, usePathname, useRouter } from "expo-router";
62+
import * as SplashScreen from "expo-splash-screen";
5663

57-
```js
58-
import { DetourProvider, type Config } from '@swmansion/react-native-detour';
64+
import { type Config, DetourProvider, useDetourContext } from "@swmansion/react-native-detour";
65+
66+
SplashScreen.preventAutoHideAsync();
5967

6068
const config: Config = {
61-
apiKey: '<REPLACE_WITH_YOUR_API_KEY>',
62-
appID: '<REPLACE_WITH_APP_ID_FROM_PLATFORM>',
63-
shouldUseClipboard: true,
69+
apiKey: "<REPLACE_WITH_YOUR_API_KEY>",
70+
appID: "<REPLACE_WITH_APP_ID_FROM_PLATFORM>",
6471
};
6572

6673
export default function RootLayout() {
@@ -70,19 +77,8 @@ export default function RootLayout() {
7077
</DetourProvider>
7178
);
7279
}
73-
```
7480

75-
### Example (Expo Router)
76-
77-
```js
78-
import { Stack, usePathname, useRouter } from "expo-router";
79-
import * as SplashScreen from "expo-splash-screen";
80-
81-
import { useDetourContext } from "@swmansion/react-native-detour";
82-
83-
SplashScreen.preventAutoHideAsync();
84-
85-
export function RootNavigator() {
81+
function RootNavigator() {
8682
const { isLinkProcessed, link, clearLink } = useDetourContext();
8783
const pathname = usePathname();
8884
const router = useRouter();
@@ -99,7 +95,7 @@ export function RootNavigator() {
9995
router.replace({ pathname: link.pathname, params: link.params });
10096
return;
10197
}
102-
clearLink(); // avoid redirecting again when returning to this screen
98+
clearLink();
10399
}, [clearLink, isLinkProcessed, link, pathname, router]);
104100

105101
if (!isLinkProcessed) {
@@ -110,14 +106,35 @@ export function RootNavigator() {
110106
}
111107
```
112108

113-
Learn more about usage from our [docs](https://docs.swmansion.com/detour/docs/SDK/sdk-usage)
109+
</details>
114110

115-
### React Navigation linking integration
111+
### React Navigation
116112

117-
When integrating with React Navigation's custom linking API (`getInitialURL` + `subscribe`), use Detour as the URL source:
113+
#### v2.3.0 and later
118114

119-
```ts
120-
import { DETOUR_LINKING_PREFIX, Detour } from "@swmansion/react-native-detour";
115+
Pass Detour's linking adapter to `NavigationContainer`. React Navigation will handle routing automatically — `useDetourContext` is not needed for basic usage. The splash screen is hidden via `onReady`, which fires after `getInitialURL` resolves.
116+
117+
<details>
118+
<summary>React Navigation example</summary>
119+
120+
```tsx
121+
import * as SplashScreen from "expo-splash-screen";
122+
123+
import { NavigationContainer } from "@react-navigation/native";
124+
125+
import {
126+
type Config,
127+
DETOUR_LINKING_PREFIX,
128+
Detour,
129+
DetourProvider,
130+
} from "@swmansion/react-native-detour";
131+
132+
SplashScreen.preventAutoHideAsync();
133+
134+
const config: Config = {
135+
apiKey: "<REPLACE_WITH_YOUR_API_KEY>",
136+
appID: "<REPLACE_WITH_APP_ID_FROM_PLATFORM>",
137+
};
121138

122139
const linking = {
123140
prefixes: [DETOUR_LINKING_PREFIX],
@@ -128,39 +145,62 @@ const linking = {
128145
const subscription = Detour.addEventListener("url", ({ url }) => {
129146
listener(url);
130147
});
131-
132148
return () => subscription.remove();
133149
},
134150
};
151+
152+
export function App() {
153+
return (
154+
<DetourProvider config={config}>
155+
<NavigationContainer linking={linking} onReady={() => SplashScreen.hideAsync()}>
156+
<Navigation />
157+
</NavigationContainer>
158+
</DetourProvider>
159+
);
160+
}
135161
```
136162

137-
`DETOUR_LINKING_PREFIX` is an internal adapter prefix used for Detour-resolved routes.
138-
This API requires `DetourProvider` to be mounted above your `NavigationContainer`.
163+
</details>
164+
165+
#### Before v2.3.0
139166

140-
See React Navigation docs:
141-
https://reactnavigation.org/docs/deep-linking?config=static#integrating-with-other-tools
167+
Use `useDetourContext` and call your navigator imperatively, the same way as the [Expo Router approach](#expo-router) above.
168+
169+
#### Auth-gated apps
170+
171+
The adapter appends `fromDeepLink=true` and `linkType` query params to every URL it emits — you will see these in your route params.
142172

143173
For auth-gated apps, let React Navigation hold the deep link until the right screen is reachable.
144174
Render screens conditionally on auth/onboarding state and opt in to React Navigation's pending-link
145175
behavior on the navigator:
146176

177+
<details>
178+
<summary>Auth-gated navigator example</summary>
179+
147180
```tsx
148181
<Stack.Navigator UNSTABLE_routeNamesChangeBehavior="lastUnhandled">
149-
{isSignedIn
150-
? isOnboardingCompleted
151-
? <>
152-
<Stack.Screen name="Tabs" component={TabNavigator} />
153-
<Stack.Screen name="Details" component={Details} />
154-
</>
155-
: <Stack.Screen name="Onboarding" component={Onboarding} />
156-
: <Stack.Screen name="SignIn" component={SignIn} />}
182+
{isSignedIn ? (
183+
isOnboardingCompleted ? (
184+
<>
185+
<Stack.Screen name="Tabs" component={TabNavigator} />
186+
<Stack.Screen name="Details" component={Details} />
187+
</>
188+
) : (
189+
<Stack.Screen name="Onboarding" component={Onboarding} />
190+
)
191+
) : (
192+
<Stack.Screen name="SignIn" component={SignIn} />
193+
)}
157194
</Stack.Navigator>
158195
```
159196

160-
A deep link that arrives while the user is signed-out is parsed, found unreachable (the target
161-
screen isn't currently rendered), and remembered. When the rendered screen set changes — after
162-
sign-in, then again after onboarding — React Navigation retries and lands the user on the target.
163-
See `examples/react-navigation-advanced` for a working setup.
197+
</details>
198+
199+
A deep link that arrives while the user is signed-out is parsed, found unreachable, and remembered. When the rendered screen set changes after sign-in or onboarding, React Navigation retries and lands the user on the target. See [`examples/react-navigation-advanced`](./examples/react-navigation-advanced) for a working setup.
200+
201+
See the [React Navigation deep linking docs](https://reactnavigation.org/docs/deep-linking?config=static#integrating-with-other-tools).
202+
203+
Learn more from our [docs](https://detour.swmansion.com/docs/sdk/react-native/sdk-usage).
164204

165205
### Controlling which links Detour processes
166206

@@ -172,18 +212,45 @@ Use `linkProcessingMode` to control which link sources the SDK listens to:
172212
| `'web-only'` ||||
173213
| `'deferred-only'` ||||
174214

175-
```js
215+
<details>
216+
<summary>linkProcessingMode config example</summary>
217+
218+
```ts
176219
const config: Config = {
177-
apiKey: '<REPLACE_WITH_YOUR_API_KEY>',
178-
appID: '<REPLACE_WITH_APP_ID_FROM_PLATFORM>',
220+
apiKey: "<REPLACE_WITH_YOUR_API_KEY>",
221+
appID: "<REPLACE_WITH_APP_ID_FROM_PLATFORM>",
179222
// Process Universal/App links and deferred links, but let your own
180223
// navigation layer handle custom scheme links (e.g. myapp://...).
181-
linkProcessingMode: 'web-only',
224+
linkProcessingMode: "web-only",
182225
};
183226
```
184227

228+
</details>
229+
185230
Use `'deferred-only'` when Expo Router's `+native-intent.tsx` handler is already resolving runtime Universal/App links — this prevents double-processing.
186231

232+
### Clearing handled links
233+
234+
If your app redirects based on `link` (especially in entry screens), call `clearLink()` after handling the route. This prevents repeated redirects when the user returns to the same screen.
235+
236+
## Analytics
237+
238+
The SDK includes a built-in analytics module. `DetourProvider` automatically tracks app opens for retention. You can also log custom events using the predefined `DetourEventNames` enum:
239+
240+
<details>
241+
<summary>Analytics example</summary>
242+
243+
```ts
244+
import { DetourAnalytics, DetourEventNames } from "@swmansion/react-native-detour";
245+
246+
DetourAnalytics.logEvent(DetourEventNames.Purchase);
247+
DetourAnalytics.logRetention("week_1");
248+
```
249+
250+
</details>
251+
252+
See the [analytics docs](https://detour.swmansion.com/docs/Fundamentals/analytics-detour) for the full event list and retention tracking setup.
253+
187254
## Examples
188255

189256
All example apps with Detour SDK integrated live in `examples/`:
@@ -231,10 +298,6 @@ pnpm android
231298

232299
> Running `pnpm ios` / `pnpm android` produces a development build. This is recommended over Expo Go for testing deep linking flows on a real device.
233300
234-
## Clearing handled links
235-
236-
If your app redirects based on `link` (especially in entry screens), call `clearLink()` after handling the route. This prevents repeated redirects when the user returns to the same screen.
237-
238301
## Types
239302

240303
The package exposes several types to help you with type-checking in your own codebase.
@@ -243,7 +306,10 @@ The package exposes several types to help you with type-checking in your own cod
243306

244307
This type is used to define the configuration object you pass to the DetourProvider.
245308

246-
```js
309+
<details>
310+
<summary>Config type</summary>
311+
312+
```ts
247313
export type Config = {
248314
/**
249315
* Your application ID from the Detour dashboard.
@@ -257,7 +323,8 @@ export type Config = {
257323

258324
/**
259325
* Optional: A flag to determine if the provider should check the clipboard for a deferred link.
260-
* Note: This feature is iOS-only. On Android, clipboard is never accessed regardless of this setting.
326+
* Note: This feature is iOS-only. On Android, the SDK uses the install referrer for deterministic
327+
* link matching instead; clipboard is never accessed regardless of this setting.
261328
* When enabled on iOS, it may display a permission alert to the user.
262329
* Defaults to true if not provided.
263330
*/
@@ -270,7 +337,7 @@ export type Config = {
270337
* - 'deferred-only': only deferred links (use when native-intent already handles runtime links)
271338
* Defaults to 'all'.
272339
*/
273-
linkProcessingMode?: 'all' | 'web-only' | 'deferred-only';
340+
linkProcessingMode?: "all" | "web-only" | "deferred-only";
274341

275342
/**
276343
* Optional: A custom storage adapter. Defaults to AsyncStorage if not provided.
@@ -279,11 +346,16 @@ export type Config = {
279346
};
280347
```
281348

349+
</details>
350+
282351
### DetourContextType
283352

284353
This type represents the object returned by the `useDetourContext` hook, containing the resolved link and its processing status.
285354

286-
```js
355+
<details>
356+
<summary>DetourContextType type</summary>
357+
358+
```ts
287359
export type DetourContextType = {
288360
/**
289361
* Boolean indicating if the initial link (deferred, Universal/App Link, or scheme) has been processed.
@@ -303,11 +375,16 @@ export type DetourContextType = {
303375
};
304376
```
305377

378+
</details>
379+
306380
### DetourLink
307381

308382
The resolved link object, or null if no link was found.
309383

310-
```js
384+
<details>
385+
<summary>DetourLink type</summary>
386+
387+
```ts
311388
export type DetourLink = {
312389
/** The original link URL as received by the SDK. */
313390
url: string | URL;
@@ -331,9 +408,14 @@ export type DetourLink = {
331408
} | null;
332409
```
333410

411+
</details>
412+
334413
### React Navigation adapter types
335414

336-
```js
415+
<details>
416+
<summary>React Navigation adapter types</summary>
417+
418+
```ts
337419
export const DETOUR_LINKING_PREFIX: string; // "detour://"
338420

339421
export type DetourUrlEvent = {
@@ -345,11 +427,21 @@ export type DetourUrlSubscription = {
345427
};
346428
```
347429

348-
```js
430+
```ts
349431
Detour.getInitialURL(): Promise<string | undefined>
350432
Detour.addEventListener("url", (event: DetourUrlEvent) => void): DetourUrlSubscription
351433
```
352434

435+
</details>
436+
437+
## Other Detour SDKs
438+
439+
Detour is also available for other app stacks:
440+
441+
- Android SDK: [https://github.com/software-mansion-labs/android-detour](https://github.com/software-mansion-labs/android-detour)
442+
- iOS SDK: [https://github.com/software-mansion-labs/ios-detour](https://github.com/software-mansion-labs/ios-detour)
443+
- Flutter SDK: [https://github.com/software-mansion-labs/detour-flutter-plugin](https://github.com/software-mansion-labs/detour-flutter-plugin)
444+
353445
---
354446

355447
## License

0 commit comments

Comments
 (0)