0
# Deep Linking
1
2
Deep linking enables navigation to specific screens in your app using URLs. React Navigation provides comprehensive URL-based navigation with path matching, prefixes, and state synchronization.
3
4
## Capabilities
5
6
### Linking Options Configuration
7
8
Configure deep linking behavior through the NavigationContainer's linking prop.
9
10
```typescript { .api }
11
interface LinkingOptions<ParamList extends {}> {
12
/** Whether deep link handling should be enabled. Defaults to true when linking options are provided */
13
enabled?: boolean;
14
/** URL prefixes to match against. Stripped from URLs before parsing */
15
prefixes: string[];
16
/** Optional function to filter which URLs should be handled */
17
filter?: (url: string) => boolean;
18
/** Path configuration for mapping URLs to navigation state */
19
config?: {
20
/** Root path for the entire navigation tree */
21
path?: string;
22
/** Screen path configurations */
23
screens: PathConfigMap<ParamList>;
24
/** Name of the initial route for the root navigator */
25
initialRouteName?: keyof ParamList;
26
};
27
/** Custom function to get the initial URL. Defaults to Linking.getInitialURL() */
28
getInitialURL?: () => string | null | undefined | Promise<string | null | undefined>;
29
/** Custom function to subscribe to URL updates */
30
subscribe?: (listener: (url: string) => void) => undefined | void | (() => void);
31
/** Custom function to parse URL to navigation state */
32
getStateFromPath?: typeof getStateFromPath;
33
/** Custom function to convert navigation state to URL */
34
getPathFromState?: typeof getPathFromState;
35
/** Custom function to convert navigation state to action */
36
getActionFromState?: typeof getActionFromState;
37
}
38
39
type PathConfigMap<ParamList> = {
40
[RouteName in keyof ParamList]: string | PathConfig<ParamList[RouteName]>;
41
};
42
43
interface PathConfig<ParamList> {
44
path?: string;
45
exact?: boolean;
46
screens?: PathConfigMap<ParamList>;
47
initialRouteName?: keyof ParamList;
48
parse?: { [Param in keyof ParamList]?: (value: string) => ParamList[Param] };
49
stringify?: { [Param in keyof ParamList]?: (value: ParamList[Param]) => string };
50
}
51
```
52
53
**Usage Examples:**
54
55
```typescript
56
import { NavigationContainer } from '@react-navigation/native';
57
58
// Basic deep linking setup
59
const linking = {
60
prefixes: ['myapp://'],
61
config: {
62
screens: {
63
Home: 'home',
64
Profile: 'profile/:id',
65
Settings: 'settings',
66
},
67
},
68
};
69
70
function App() {
71
return (
72
<NavigationContainer linking={linking}>
73
{/* Your navigators */}
74
</NavigationContainer>
75
);
76
}
77
78
// Advanced configuration with nested navigators
79
const advancedLinking = {
80
prefixes: ['myapp://', 'https://myapp.com'],
81
config: {
82
screens: {
83
Auth: {
84
screens: {
85
Login: 'login',
86
Register: 'register',
87
},
88
},
89
Main: {
90
screens: {
91
Home: 'home',
92
Profile: {
93
path: 'profile/:id',
94
parse: {
95
id: (id: string) => parseInt(id, 10),
96
},
97
},
98
Settings: {
99
path: 'settings',
100
screens: {
101
Account: 'account',
102
Privacy: 'privacy',
103
},
104
},
105
},
106
},
107
},
108
},
109
};
110
111
// With URL filtering
112
const filteredLinking = {
113
prefixes: ['myapp://'],
114
filter: (url: string) => !url.includes('oauth-callback'),
115
config: {
116
screens: {
117
Home: 'home',
118
Profile: 'profile/:id',
119
},
120
},
121
};
122
```
123
124
### URL Prefixes
125
126
Define which URL schemes and hosts your app should handle.
127
128
```typescript { .api }
129
interface LinkingOptions<ParamList> {
130
/**
131
* The prefixes are stripped from the URL before parsing them.
132
* Usually they are the scheme + host (e.g. myapp://chat?user=jane)
133
* This is not supported on Web.
134
*/
135
prefixes: string[];
136
}
137
```
138
139
**Usage Examples:**
140
141
```typescript
142
// App-specific scheme
143
const linking = {
144
prefixes: ['myapp://'],
145
// myapp://profile/123 -> profile/123
146
};
147
148
// Universal links
149
const universalLinking = {
150
prefixes: ['https://example.com'],
151
// https://example.com/profile/123 -> profile/123
152
};
153
154
// Multiple prefixes with wildcards
155
const multiPrefixLinking = {
156
prefixes: [
157
'myapp://',
158
'https://example.com',
159
'https://*.example.com', // Matches any subdomain
160
],
161
};
162
163
// Development and production
164
const environmentLinking = {
165
prefixes: [
166
'myapp://',
167
__DEV__
168
? 'https://dev.example.com'
169
: 'https://example.com',
170
],
171
};
172
```
173
174
### Path Configuration
175
176
Map URL paths to navigation screens with parameter parsing.
177
178
```typescript { .api }
179
interface PathConfig<ParamList> {
180
/** Path pattern to match (e.g., 'profile/:id') */
181
path?: string;
182
/** Whether the path should match exactly */
183
exact?: boolean;
184
/** Nested screen configurations */
185
screens?: PathConfigMap<ParamList>;
186
/** Initial route name for nested navigators */
187
initialRouteName?: keyof ParamList;
188
/** Functions to parse URL parameters */
189
parse?: { [Param in keyof ParamList]?: (value: string) => ParamList[Param] };
190
/** Functions to stringify parameters for URL generation */
191
stringify?: { [Param in keyof ParamList]?: (value: ParamList[Param]) => string };
192
}
193
```
194
195
**Usage Examples:**
196
197
```typescript
198
// Simple path mapping
199
const config = {
200
screens: {
201
Home: 'home',
202
About: 'about',
203
Contact: 'contact',
204
},
205
};
206
207
// Paths with parameters
208
const paramsConfig = {
209
screens: {
210
Profile: 'profile/:id',
211
Article: 'article/:slug',
212
Category: 'category/:name/page/:page',
213
},
214
};
215
216
// Parameter parsing and validation
217
const parsedConfig = {
218
screens: {
219
Profile: {
220
path: 'profile/:id',
221
parse: {
222
id: (id: string) => {
223
const parsed = parseInt(id, 10);
224
return isNaN(parsed) ? 0 : parsed;
225
},
226
},
227
},
228
Search: {
229
path: 'search',
230
parse: {
231
q: (query: string) => decodeURIComponent(query),
232
page: (page: string) => Math.max(1, parseInt(page, 10)),
233
},
234
},
235
},
236
};
237
238
// Nested navigator configuration
239
const nestedConfig = {
240
screens: {
241
Home: 'home',
242
Shop: {
243
path: 'shop',
244
screens: {
245
ProductList: 'products',
246
ProductDetail: 'product/:id',
247
Cart: 'cart',
248
Checkout: {
249
path: 'checkout',
250
screens: {
251
Shipping: 'shipping',
252
Payment: 'payment',
253
Confirmation: 'confirmation',
254
},
255
},
256
},
257
},
258
},
259
};
260
```
261
262
### URL Filtering
263
264
Control which URLs should be handled by your navigation system.
265
266
```typescript { .api }
267
interface LinkingOptions<ParamList> {
268
/**
269
* Optional function which takes an incoming URL and returns a boolean
270
* indicating whether React Navigation should handle it.
271
* This can be used to disable deep linking for specific URLs.
272
*/
273
filter?: (url: string) => boolean;
274
}
275
```
276
277
**Usage Examples:**
278
279
```typescript
280
// Filter out authentication callbacks
281
const authFilterLinking = {
282
prefixes: ['myapp://'],
283
filter: (url: string) => !url.includes('oauth-callback'),
284
config: {
285
screens: {
286
Home: 'home',
287
Profile: 'profile/:id',
288
},
289
},
290
};
291
292
// Filter based on URL patterns
293
const patternFilterLinking = {
294
prefixes: ['myapp://'],
295
filter: (url: string) => {
296
// Don't handle admin URLs
297
if (url.includes('/admin/')) return false;
298
// Don't handle external redirects
299
if (url.includes('redirect=')) return false;
300
// Handle everything else
301
return true;
302
},
303
};
304
```
305
306
### Custom URL Handling
307
308
Implement custom logic for URL processing and state management.
309
310
```typescript { .api }
311
interface LinkingOptions<ParamList> {
312
/** Custom function to get the initial URL used for linking */
313
getInitialURL?: () => string | null | undefined | Promise<string | null | undefined>;
314
/** Custom function to subscribe to URL updates */
315
subscribe?: (listener: (url: string) => void) => undefined | void | (() => void);
316
/** Custom function to parse URL to navigation state (advanced) */
317
getStateFromPath?: typeof getStateFromPath;
318
/** Custom function to convert state to URL (advanced) */
319
getPathFromState?: typeof getPathFromState;
320
/** Custom function to convert state to action (advanced) */
321
getActionFromState?: typeof getActionFromState;
322
}
323
```
324
325
**Usage Examples:**
326
327
```typescript
328
import { Linking } from 'react-native';
329
330
// Custom initial URL handling
331
const customInitialURL = {
332
prefixes: ['myapp://'],
333
getInitialURL: async () => {
334
// Check if app was opened from a deep link
335
const url = await Linking.getInitialURL();
336
337
// Custom logic for handling the initial URL
338
if (url?.includes('special-link')) {
339
// Transform or validate the URL
340
return url.replace('special-link', 'normal-link');
341
}
342
343
return url;
344
},
345
};
346
347
// Custom URL subscription
348
const customSubscription = {
349
prefixes: ['myapp://'],
350
subscribe: (listener: (url: string) => void) => {
351
// Custom URL change handling
352
const onReceiveURL = ({ url }: { url: string }) => {
353
// Apply custom transformations or filtering
354
if (url.includes('valid-prefix')) {
355
listener(url);
356
}
357
};
358
359
const subscription = Linking.addEventListener('url', onReceiveURL);
360
361
return () => subscription?.remove();
362
},
363
};
364
365
// Custom state parsing
366
const customStateParsing = {
367
prefixes: ['myapp://'],
368
getStateFromPath: (path: string, config: any) => {
369
// Custom logic to convert path to navigation state
370
if (path.startsWith('/legacy/')) {
371
// Handle legacy URL format
372
const newPath = path.replace('/legacy/', '/');
373
return getStateFromPath(newPath, config);
374
}
375
376
// Use default parsing for other paths
377
return getStateFromPath(path, config);
378
},
379
};
380
```
381
382
### Error Handling
383
384
Handle cases where deep links cannot be processed or lead to invalid states.
385
386
```typescript { .api }
387
// Invalid URLs that don't match any configured path will be ignored
388
// Use NavigationContainer's onUnhandledAction prop to handle edge cases
389
390
function App() {
391
const handleUnhandledAction = (action: NavigationAction) => {
392
console.warn('Unhandled navigation action:', action);
393
// Custom error handling or fallback navigation
394
};
395
396
return (
397
<NavigationContainer
398
linking={linking}
399
onUnhandledAction={handleUnhandledAction}
400
>
401
{/* Your navigators */}
402
</NavigationContainer>
403
);
404
}
405
```