React Native course Β· Module 3: Navigation
Deep Linking - Magic Portals
In this lesson8
Deep Linking is like magic portals in Atlantis - they allow users to jump directly to specific screens of the application from external links.
What is Deep Linking?
Deep Linking enables:
- Opening the application from URL links
- Navigating directly to a specific screen
- Passing parameters through URL
- Handling push notifications
Configuration in Expo
// app.json
{
"expo": {
"scheme": "atlantis",
"web": {
"bundler": "metro"
}
}
}Configuring linking in NavigationContainer
import { NavigationContainer, LinkingOptions } from '@react-navigation/native';
import * as Linking from 'expo-linking';
// URL prefix for the application
const prefix = Linking.createURL('/');
type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Item: { itemId: number };
Settings: undefined;
};
const linking: LinkingOptions<RootStackParamList> = {
prefixes: [prefix, 'atlantis://', 'https://atlantis.app'],
config: {
screens: {
Home: '',
Profile: 'user/:userId',
Item: {
path: 'item/:itemId',
parse: {
itemId: (itemId: string) => parseInt(itemId, 10),
},
},
Settings: 'settings',
// Nested navigators
MainTabs: {
screens: {
Feed: 'feed',
Messages: 'messages/:conversationId?',
},
},
},
},
};
function App() {
return (
<NavigationContainer
linking={linking}
fallback={<LoadingScreen />}
>
<Stack.Navigator>
{/* Screens */}
</Stack.Navigator>
</NavigationContainer>
);
}Supported URL Formats
atlantis:// β Home
atlantis://user/123 β Profile { userId: '123' }
atlantis://item/42 β Item { itemId: 42 }
atlantis://settings β Settings
atlantis://feed β MainTabs > Feed
atlantis://messages/abc β MainTabs > Messages { conversationId: 'abc' }Creating Links Within the App
import * as Linking from 'expo-linking';
function ShareButton({ itemId }: { itemId: number }) {
const handleShare = async () => {
// Internal link (to the app)
const internalUrl = Linking.createURL(`item/${itemId}`);
console.log(internalUrl); // exp://192.168.1.5:8081/--/item/42
// Universal link (web + app)
const universalUrl = `https://atlantis.app/item/${itemId}`;
// Sharing
await Share.share({
message: `Check out this item: ${universalUrl}`,
url: universalUrl,
});
};
return (
<Pressable onPress={handleShare}>
<Text>Share</Text>
</Pressable>
);
}Opening External URLs
import * as Linking from 'expo-linking';
function ExternalLinks() {
const openWebsite = async () => {
await Linking.openURL('https://atlantis.app');
};
const openSettings = async () => {
// Opens the system settings for the app
await Linking.openSettings();
};
const sendEmail = async () => {
await Linking.openURL('mailto:contact@atlantis.app?subject=Question');
};
const makeCall = async () => {
await Linking.openURL('tel:+48123456789');
};
const openMaps = async () => {
// iOS
await Linking.openURL('maps://app?daddr=52.2297,21.0122');
// Android
await Linking.openURL('geo:52.2297,21.0122');
};
return (
<View>
<Pressable onPress={openWebsite}><Text>Website</Text></Pressable>
<Pressable onPress={openSettings}><Text>Settings</Text></Pressable>
<Pressable onPress={sendEmail}><Text>Email</Text></Pressable>
<Pressable onPress={makeCall}><Text>Call</Text></Pressable>
</View>
);
}Listening for Links
import * as Linking from 'expo-linking';
import { useEffect } from 'react';
function App() {
useEffect(() => {
// Check if the app was opened from a link
const getInitialURL = async () => {
const initialUrl = await Linking.getInitialURL();
if (initialUrl) {
console.log('Opened from link:', initialUrl);
}
};
getInitialURL();
// Listen for new links (when the app is in the background)
const subscription = Linking.addEventListener('url', ({ url }) => {
console.log('Received link:', url);
});
return () => subscription.remove();
}, []);
return <NavigationContainer linking={linking}>{/* ... */}</NavigationContainer>;
}State Persistence (Preserving Navigation State)
import AsyncStorage from '@react-native-async-storage/async-storage';
const NAVIGATION_STATE_KEY = 'NAVIGATION_STATE';
function App() {
const [isReady, setIsReady] = useState(false);
const [initialState, setInitialState] = useState();
useEffect(() => {
const restoreState = async () => {
try {
const savedState = await AsyncStorage.getItem(NAVIGATION_STATE_KEY);
if (savedState) {
setInitialState(JSON.parse(savedState));
}
} finally {
setIsReady(true);
}
};
restoreState();
}, []);
if (!isReady) return <LoadingScreen />;
return (
<NavigationContainer
initialState={initialState}
onStateChange={(state) => {
AsyncStorage.setItem(NAVIGATION_STATE_KEY, JSON.stringify(state));
}}
>
{/* ... */}
</NavigationContainer>
);
}Deep Linking significantly improves the UX of your application, enabling sharing of specific content and integration with push notifications.
Code for this lesson: App.tsx
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import * as Linking from 'expo-linking';
const Stack = createNativeStackNavigator();
// App URL prefix, e.g. atlantis://item/42
const prefix = Linking.createURL('/');
const linking = {
prefixes: [prefix],
// TODO: complete the config mapping paths to screens:
// config: {
// screens: {
// Home: '',
// Details: 'item/:itemId',
// },
// },
};
function HomeScreen() {
return (
<View style={styles.screen}>
<Text style={styles.title}>Portal of Atlantis</Text>
</View>
);
}
function DetailsScreen({ route }) {
return (
<View style={styles.screen}>
<Text style={styles.title}>Item #{route.params?.itemId}</Text>
</View>
);
}
export default function App() {
return (
// TODO: pass the linking={linking} prop to <NavigationContainer>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#1a5276',
},
title: {
fontSize: 22,
color: '#fff',
fontWeight: '600',
},
});Docs entries used in this lesson
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What does the 'scheme' property in app.json define?
2. Which method opens an external URL or application?
Hands-on tasks in the game
- Horizontal ordering
Arrange creating a URL to share:
- Code editor
Complete the linking configuration.