Deep Linking
Deep linking lets users open a specific screen in your app from a URL — whether from a push notification, a web link, or another app. React Navigation maps URLs to screens through a linking configuration on NavigationContainer.
The Linking Config
Define a mapping from URL paths to screen names:
const linking = {
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: '',
Details: 'details/:id',
Profile: 'profile',
},
},
};
prefixes— the URL schemes your app handles.myapp://is a custom scheme;https://myapp.comis a universal link.config.screens— maps screen names to URL patterns.details/:idmeans a URL likemyapp://details/42navigates to the Details screen with{ id: '42' }as a route param.
Pass the config to NavigationContainer:
<NavigationContainer linking={linking}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
URL Params
Route params from the URL are available on route.params just like params passed via navigate:
function DetailsScreen({ route }) {
const { id } = route.params;
return (
<View style={styles.container}>
<Text>Item ID: {id}</Text>
</View>
);
}
The URL myapp://details/42 sets route.params.id to '42' (as a string — parse it if you need a number).
Nested Navigator Linking
For nested navigators, nest the config to match your navigator structure:
const linking = {
prefixes: ['myapp://'],
config: {
screens: {
Main: {
screens: {
Home: {
screens: {
HomeMain: '',
HomeDetails: 'details/:id',
},
},
Settings: 'settings',
},
},
},
},
};
This maps myapp://details/42 to: navigate to the Main screen (drawer), then the Home tab, then the HomeDetails screen with { id: '42' }.
Testing Deep Links
With Expo, test deep links from the command line:
# Custom scheme
npx uri-scheme open "myapp://details/42" --ios
npx uri-scheme open "myapp://details/42" --android
# Or with Expo's deep link URL during development
npx uri-scheme open "exp://127.0.0.1:8081/--/details/42" --ios
You can also test with Linking.openURL from inside the app:
import { Linking } from 'react-native';
<Button
title="Test Deep Link"
onPress={() => Linking.openURL('myapp://details/42')}
/>
Complete Example
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button, FlatList, TouchableOpacity, StyleSheet } from 'react-native';
const Stack = createNativeStackNavigator();
const ITEMS = [
{ id: '1', title: 'First Item' },
{ id: '2', title: 'Second Item' },
{ id: '3', title: 'Third Item' },
];
const linking = {
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: '',
Details: 'details/:id',
Profile: 'profile',
},
},
};
function HomeScreen({ navigation }) {
return (
<FlatList
data={ITEMS}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.item}
onPress={() => navigation.navigate('Details', { id: item.id })}
>
<Text style={styles.itemTitle}>{item.title}</Text>
</TouchableOpacity>
)}
/>
);
}
function DetailsScreen({ route }) {
const { id } = route.params;
const item = ITEMS.find((i) => i.id === id);
return (
<View style={styles.container}>
<Text style={styles.title}>{item?.title ?? 'Unknown Item'}</Text>
<Text>ID: {id}</Text>
</View>
);
}
function ProfileScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Profile</Text>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
title: { fontSize: 24, marginBottom: 8 },
item: { padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' },
itemTitle: { fontSize: 18 },
});
export default function App() {
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Items' }} />
<Stack.Screen name="Details" component={DetailsScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
Opening myapp://details/2 launches the app directly to the Details screen showing "Second Item". The fallback prop shows a loading indicator while the deep link is being resolved.
With deep linking configured, your app can respond to URLs from push notifications, email links, or other apps. The linking config scales — just add more screen mappings as your app grows.