Edit this page
Learn how to create links to move between pages.
Expo Router uses "links" to move between pages in the app. This is conceptually similar to how the web works with <a>
tags and the href
attribute.
app
index.tsx
about.tsx
user
[id].tsx
In the following example, there are two Link
components which navigate to different routes.
import { View } from 'react-native';
import { Link } from 'expo-router';
export default function Page() {
return (
<View>
<Link href="/about">About</Link>
{/* ...other links */}
<Link href="/user/bacon">View user</Link>
</View>
);
}
The Link component wraps the children in a <Text>
component by default, this is useful for accessibility but not always desired. You can customize the component by passing the asChild
prop, which will forward all props to the first child of the Link
component. The child component must support the onPress
and onClick
props, href
and role
will also be passed down.
import { Pressable, Text } from 'react-native';
import { Link } from 'expo-router';
export default function Page() {
return (
<Link href="/other" asChild>
<Pressable>
<Text>Home</Text>
</Pressable>
</Link>
);
}
Expo Router uses a stack-based navigation approach. Each new route you navigate to get added to a stack. If you navigate to a route already in the stack, the stack unwinds back to that existing route.
For example, when you navigate from /feed
to /profile
, the stack contains /feed
and /profile
. If you then navigate to /settings
, the stack contains /feed
, /profile
, and /settings
. If you then navigate back to /feed
, the stack unwinds back to /feed
.
To navigate to a route without the stack unwinding, you can use the push
prop on the Link
component. This always pushes the route onto the stack, even if it already exists.
In contrast, the replace
method substitutes the current route in the navigation stack with a new one, effectively replacing the current screen with the new one without adding to the stack.
To navigate, you can provide a full path (/profile/settings
), a relative path (../settings
), or by passing an object ({ pathname: 'profile', params: { id: '123' } }
).
relativeToDirectory
will be released in the next version ofexpo-router
.
A relative URL is a URL prefix with ./
, such as ./article
, or ./article/
. Relative URLs are resolved relative to the current rendered screen.
The URL of the current screen is a document URL (a URL without a trailing slash), so relative URLs will be resolved relative to the directory. This can be confusing when the current screen is rendered by an index
file as the relative URL is resolved from the URL's directory, not the file system. By using the relativeToDirectory
option, Expo Router will instead treat the current URL as a directory URL.
Href | Current URL | relativeToDirectory | Result |
---|---|---|---|
./article | /route/v1 | /route/article | |
./article | /route/v1 | true | /route/v1/article |
../article | /route/v1 | /article | |
../article | /route/v1 | true | /route/article |
// Using `relativeToDirectory` with Link
<Link href="./article" relativeToDirectory>Go to article</Link>
// Using `relativeToDirectory` with the imperative API
router.push("./article", { relativeToDirectory: true })
router.navigate("./article", { relativeToDirectory: true })
router.replace("./article", { relativeToDirectory: true })
Relative URLs without a./
prefix, such asarticle
, are not supported.
Dynamic routes and query parameters can be provided statically or with the convenience Href object.
import { Link } from 'expo-router';
import { View } from 'react-native';
export default function Page() {
return (
<View>
<Link
href={{
pathname: '/user/[id]',
params: { id: 'bacon' }
}}>
View user
</Link>
</View>
);
}
By default, links navigate
to the nearest route in the navigation stack, either by pushing a new route or unwinding to an existing route. You can use the push
prop to always push the route onto the stack.
import { Link } from 'expo-router';
export default function Page() {
return (
<View>
<Link push href="/feed">Login</Link>
</View>
);
}
By default, links "push" routes onto the navigation stack. It follows the same rules as navigation.navigate()
. This means that the previous screen will be available when the user navigates back. You can use the replace
prop to replace the current screen instead of pushing a new one.
import { Link } from 'expo-router';
export default function Page() {
return (
<View>
<Link replace href="/feed">Login</Link>
</View>
);
}
Use router.replace()
to replace the current screen imperatively.
Native navigation does not always support replace
. For example on X, you wouldn't be able to "replace" directly from a profile to a tweet, this is because the UI requires a back button to return to the feed or other top-level tab screen. In this case, replace would switch to the feed tab, and push the tweet route on top of it, or if you were on a different tweet inside the feed tab, it would replace the current tweet with the new tweet. This exact behavior can be obtained in Expo Router by using unstable_settings
.
You can also navigate imperatively using the router
object. This is useful when you need to perform a navigation action outside a React component, such as in an event handler or a utility function.
import { router } from 'expo-router';
export function logout() {
router.replace('/login');
}
The router
object is immutable and contains multiple methods to navigate. See Router
for more information about the available methods.
Expo Router can automatically generate static TypeScript types for all routes in your app. This allows you to use autocomplete for href
s and get warnings when invalid links are used. See Statically Typed Routes for more information.
Expo Router supports the standard <a>
element when running on web, however this will perform a full-page server-navigation. This is slower and doesn't take full advantage of React. Instead, the Expo Router Link
component will perform client-side navigation, which will preserve the state of the website and navigate faster.
The web-only attributes target
, rel
, and download
are also supported. These will be passed to the <a>
element when running on web.
Client-side navigation works with both single-page apps, and static rendering.
See Test the deep link to learn how to emulate deep links in Android Emulators and iOS Simulators.