Configuring links
In this guide, we will configure React Navigation to handle external links. This is necessary if you want to:
- Handle deep links in React Native apps on Android and iOS
- Enable URL integration in browser when using on web
- Use
<Link />oruseLinkToto navigate using paths.
Make sure that you have configured deep links in your app before proceeding. If you have an Android or iOS app, remember to specify the prefixes option.
- Static
- Dynamic
The Navigation component accepts a linking prop that makes it easier to handle incoming links:
import { createStaticNavigation } from '@react-navigation/native';
const linking = {
enabled: 'auto' /* Automatically generate paths for all screens */,
prefixes: [
/* your linking prefixes */
],
};
function App() {
return (
<Navigation
linking={linking}
fallback={<Text>Loading...</Text>}
/>
);
}
const Navigation = createStaticNavigation(RootStack);
The NavigationContainer accepts a linking prop that makes it easier to handle incoming links. The 2 of the most important properties you can specify in the linking prop are prefixes and config:
import { NavigationContainer } from '@react-navigation/native';
const linking = {
prefixes: [
/* your linking prefixes */
],
config: {
/* configuration for matching screens with paths */
},
};
function App() {
return (
<NavigationContainer
linking={linking}
fallback={<Text>Loading...</Text>}
>
{/* content */}
</NavigationContainer>
);
}
When you specify the linking prop, React Navigation will handle incoming links automatically. On Android and iOS, it'll use React Native's Linking module to handle incoming links, both when the app was opened with the link, and when new links are received when the app is open. On the Web, it'll use the History API to sync the URL with the browser.
You can also pass a fallback prop that controls what's displayed when React Navigation is trying to resolve the initial deep link URL.
When passing a linking prop, define the linking object at the module-level or memoize it with useMemo so that React Navigation can cache the processed configuration.
Prefixes
The prefixes option can be used to specify custom schemes (e.g. example://) as well as host & domain names (e.g. https://example.com) if you have configured Universal Links or Android App Links.
For example:
const linking = {
prefixes: ['example://', 'https://example.com'],
};
The prefixes option has no effect on Web. The host & domain names will be automatically determined from the Website URL in the browser.
Multiple subdomains
To match all subdomains of an associated domain, you can specify a wildcard by prefixing *. before the beginning of a specific domain. Note that an entry for *.example.com does not match example.com because of the period after the asterisk. To enable matching for both *.example.com and example.com, you need to provide a separate prefix entry for each.
const linking = {
prefixes: ['example://', 'https://example.com', 'https://*.example.com'],
};
Filtering certain paths
Sometimes we may not want to handle all incoming links. For example, we may want to filter out links meant for authentication (e.g. expo-auth-session) or other purposes instead of navigating to a specific screen.
To achieve this, you can use the filter option:
const linking = {
prefixes: ['example://', 'https://example.com'],
filter: (url) => !url.includes('+expo-auth-session'),
};
This is not supported on Web as we always need to handle the URL of the page.
Apps under subpaths
If your app is hosted under a subpath, you can specify the subpath at the top-level of the config. For example, if your app is hosted at https://example.com/app, you can specify the path as app:
const linking = {
prefixes: ['example://', 'https://example.com'],
config: {
path: 'app',
// ...
},
};
It's not possible to specify params here since this doesn't belong to a screen, e.g. app/:id won't work.
Mapping path to route names
- Static
- Dynamic
If you specify enabled: 'auto' in the linking prop, React Navigation will automatically generate paths for all screens. For example, if you have a Profile screen in the navigator, it'll automatically generate a path for it as profile.
If you wish to handle the configuration manually, or want to override the generated path for a specific screen, you can specify linking property next to the screen in the navigator to map a path to a screen. For example:
const RootStack = createStackNavigator({
screens: {
Profile: {
screen: ProfileScreen,
linking: {
path: 'user',
},
},
Chat: {
screen: ChatScreen,
linking: {
path: 'feed/:sort',
},
},
},
});
In this example:
Chatscreen that handles the URL/feedwith the paramsort(e.g./feed/latest- theChatscreen will receive a paramsortwith the valuelatest).Profilescreen that handles the URL/user.
Similarly, when you have a nested navigator, you can specify the linking property for the screens in the navigator to handle the path for the nested screens:
const HomeTabs = createBottomTabNavigator({
screens: {
Home: {
screen: HomeScreen,
linking: {
path: 'home',
},
},
Settings: {
screen: SettingsScreen,
linking: {
path: 'settings',
},
},
},
});
const RootStack = createStackNavigator({
screens: {
HomeTabs: {
screen: HomeTabs,
},
Profile: {
screen: ProfileScreen,
linking: {
path: 'user',
},
},
Chat: {
screen: ChatScreen,
linking: {
path: 'feed/:sort',
},
},
},
});
In the above example, the following path formats are handled:
/homenavigates to theHomeTabs->Homescreen/settingsnavigates to theHomeTabs->Settingsscreen/usernavigates to theProfilescreen/feed/:sortnavigates to theChatscreen with the paramsort
How does automatic path generation work?
When using automatic path generation with enabled: 'auto', the following rules are applied:
- Screens with an explicit
linkingproperty are not used for path generation and will be added as-is. - Screen names will be converted from
PascalCasetokebab-caseto use as the path (e.g.NewsFeed->news-feed). - Unless a screen has explicit empty path (
path: '') to use for the homepage, the first leaf screen encountered will be used as the homepage. - Path generation only handles leaf screens, i.e. no path is generated for screens containing nested navigators. It's still possible to specify a path for them with an explicit
linkingproperty.
Let's say we have the following navigation structure:
const HomeTabs = createBottomTabNavigator({
screens: {
Home: {
screen: HomeScreen,
},
Settings: {
screen: SettingsScreen,
},
},
});
const RootStack = createStackNavigator({
screens: {
HomeTabs: {
screen: HomeTabs,
},
Profile: {
screen: ProfileScreen,
},
Chat: {
screen: ChatScreen,
},
},
});
With automatic path generation, the following paths will be generated:
/navigates to theHomeTabs->Homescreen/settingsnavigates to theHomeTabs->Settingsscreen/profilenavigates to theProfilescreen/chatnavigates to theChatscreen
If the URL contains a query string, it'll be passed as params to the screen. For example, the URL /profile?user=jane will pass the user param to the Profile screen.
If you specify a linking option, by default React Navigation will use the path segments as the route name when parsing the URL. However, directly translating path segments to route names may not be the expected behavior.
You can specify the config option in linking to control how the deep link is parsed to suit your needs. The config should specify the mapping between route names and path patterns:
const config = {
screens: {
Chat: 'feed/:sort',
Profile: 'user',
},
};
In this example:
Chatscreen that handles the URL/feedwith the paramsort(e.g./feed/latest- theChatscreen will receive a paramsortwith the valuelatest).Profilescreen that handles the URL/user.
The config option can then be passed in the linking prop to the container:
import { NavigationContainer } from '@react-navigation/native';
const config = {
screens: {
Chat: 'feed/:sort',
Profile: 'user',
},
};
const linking = {
prefixes: ['https://example.com', 'example://'],
config,
};
function App() {
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
{/* content */}
</NavigationContainer>
);
}
The config object must match the navigation structure for your app. For example, the above configuration is if you have Chat and Profile screens in the navigator at the root:
function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Chat" component={ChatScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}
If your Chat screen is inside a nested navigator, we'd need to account for that. For example, consider the following structure where your Profile screen is at the root, but the Chat screen is nested inside Home:
function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}
function HomeScreen() {
return (
<Tab.Navigator>
<Tab.Screen name="Chat" component={ChatScreen} />
</Tab.Navigator>
);
}
For the above structure, our configuration will look like this:
const config = {
screens: {
Home: {
screens: {
Chat: 'feed/:sort',
},
},
Profile: 'user',
},
};
Similarly, any nesting needs to be reflected in the configuration.
How it works
The linking works by translating the URL to a valid navigation state and vice versa using the configuration provided. For example, the path /rooms/chat?user=jane may be translated to a state object like this:
const state = {
routes: [
{
name: 'rooms',
state: {
routes: [
{
name: 'chat',
params: { user: 'jane' },
},
],
},
},
],
};
For example, you might want to parse the path /feed/latest to something like:
const state = {
routes: [
{
name