HomeGuidesReferenceLearn

Navigate between pages

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.js
 about.js
 user
  [id].js

In the following example, there are two <Link /> components which navigate to different routes.

app/index.js
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>
  );
}

Buttons

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>
  );
}

Understanding native navigation

Expo Router uses a stack-based navigation approach. Each new route you navigate to gets added to a stack. If you navigate 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.

Imperative navigation

You may want to navigate from a global store when a user logs in or out. You can use the router object or useRouter hook to navigate imperatively (outside of React).

import { router } from 'expo-router';

export function logout() {
  router.replace('/login');
}

The router object is immutable and contains the following functions:

  • navigate: (href: Href) => void Navigate to a route using the default behavior. You can provide a full path like /profile/settings or a relative path like ../settings. Navigate to dynamic routes by passing an object like { pathname: 'profile', params: { id: '123' } }.
  • push: (href: Href) => void Navigate to a route by always pushing onto the stack. You can provide a full path like /profile/settings or a relative path like ../settings. Navigate to dynamic routes by passing an object like { pathname: 'profile', params: { id: '123' } }.
  • replace: (href: Href) => void Navigate to a route by substituting the current route with a new one. This is useful for redirects.
  • back: () => void Navigate back to previous route.
  • canGoBack: () => boolean Returns true if a valid history stack exists and the back() function can pop back.
  • setParams: (params: Record<string, string>) => void Update the query params for the currently selected route.

Linking to dynamic routes

Dynamic routes and query parameters can be provided statically or with the convenience Href object.

app/index.js
import { Link } from 'expo-router';

export default function Page() {
  return (
    <View>
      <Link
        href={{
          pathname: "/user/[id]",
          params: { id: 'bacon' }
        }}>
          View user
        </Link>
    </View>
  );
}

Pushing screens

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.

app/index.js
import { Link } from 'expo-router';

export default function Page() {
  return (
    <View>
      <Link push href="/feed">Login</Link>
    </View>
  );
}

Replacing screens

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.

app/index.js
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 Twitter, 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.

Autocomplete

Expo Router can automatically generate static TypeScript types for all routes in your app. This allows you to use autocomplete for hrefs and get warnings when invalid links are used. Learn more: Statically Typed Routes.

Web behavior

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.

Usage in simulators

See the testing URLs guide to learn how you can emulate deep links in simulators and emulators.