ExpoExpo RouterReact NativeNavigation

Expo Router Tutorial: File-Based Navigation in React Native

Learn Expo Router from scratch — file-based routes, stack and tab navigation, dynamic routes, route params, and programmatic navigation. A practical SDK 54+ guide with copy-paste examples.

DeveloperMill3 min read

Expo Router is the most-searched Expo topic for a reason: it makes navigation feel like the web. Instead of registering navigators and screens by hand, you create files in an app/ folder and each one becomes a route. This guide covers everything you need to ship real navigation — stacks, tabs, dynamic routes, params, and programmatic navigation — with minimal SDK 54+ examples.

How file-based routing works

The rule is simple: a file in app/ is a screen, and its path is its URL.

app/
  _layout.tsx      → the root navigator
  index.tsx        → "/"
  about.tsx        → "/about"
  product/
    [id].tsx       → "/product/:id"  (dynamic)
  (tabs)/
    _layout.tsx    → tab navigator
    home.tsx       → "/home"
    settings.tsx   → "/settings"

Folders wrapped in parentheses like (tabs) are route groups — they organize files without adding a segment to the URL.

New to project layout? See our companion guide on the Expo Router folder structure.

Set up the root layout

Every app/ directory needs a root _layout.tsx that defines the top-level navigator. A stack is the most common choice:

// app/_layout.tsx
import { Stack } from "expo-router"

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="index" options={{ title: "Home" }} />
      <Stack.Screen name="product/[id]" options={{ title: "Product" }} />
    </Stack>
  )
}

Link between screens

Use the <Link> component for declarative navigation — it works just like an anchor tag:

// app/index.tsx
import { Link } from "expo-router"
import { Text, View } from "react-native"

export default function Home() {
  return (
    <View style={{ flex: 1, justifyContent: "center", alignItems: "center", gap: 12 }}>
      <Text>Home screen</Text>
      <Link href="/about">About</Link>
      <Link href="/product/42">View product #42</Link>
    </View>
  )
}

Dynamic routes and reading params

A file named [id].tsx matches any value in that segment. Read the value with useLocalSearchParams():

// app/product/[id].tsx
import { useLocalSearchParams } from "expo-router"
import { Text, View } from "react-native"

export default function Product() {
  const { id } = useLocalSearchParams<{ id: string }>()

  return (
    <View>
      <Text>Showing product {id}</Text>
    </View>
  )
}

Navigate programmatically

For navigation triggered by logic (after a form submit, a login, etc.) use the useRouter() hook:

import { useRouter } from "expo-router"
import { Button } from "react-native"

export default function LoginButton() {
  const router = useRouter()

  return (
    <Button
      title="Sign in"
      onPress={() => {
        // ...run your auth logic, then:
        router.replace("/home") // replace() so users can't go "back" to login
      }}
    />
  )
}

router.push() adds a screen to the stack, router.replace() swaps the current one, and router.back() pops.

Add tab navigation

Create a (tabs) group with its own _layout.tsx using Tabs:

// app/(tabs)/_layout.tsx
import { Tabs } from "expo-router"

export default function TabLayout() {
  return (
    <Tabs>
      <Tabs.Screen name="home" options={{ title: "Home" }} />
      <Tabs.Screen name="settings" options={{ title: "Settings" }} />
    </Tabs>
  )
}

Any file inside (tabs)/ — like home.tsx and settings.tsx — becomes a tab.

Common gotchas

  • Nothing renders? You must have an app/_layout.tsx. It's the entry point.
  • Typed routes: enable experiments.typedRoutes in app.json to get autocomplete and type-safety on href values.
  • Deep linking: set a scheme in app.json (e.g. "scheme": "myapp") so URLs like myapp://product/42 open the right screen automatically.

Next steps

Once navigation is wired up, most apps immediately need auth-gated routes. See how to add authentication to an Expo app for protecting screens with a root layout, and browse our Expo templates for full apps that already use Expo Router.

Frequently asked questions

What is Expo Router?

Expo Router is a file-based routing library for React Native and web. Every file you add to the `app/` directory becomes a screen, folders become nested navigators, and deep linking works automatically. It's built on React Navigation but removes almost all navigator boilerplate.

How do I pass data between screens in Expo Router?

Use route parameters. Navigate with a path like `/product/42` (or `router.push({ pathname: '/product/[id]', params: { id: 42 } })`) and read it on the target screen with the `useLocalSearchParams()` hook.

Do I need to install Expo Router separately?

New apps created with `npx create-expo-app` already include Expo Router. To add it to an existing project, install it with `npx expo install expo-router` and set your app entry to `expo-router/entry`, plus add a URL `scheme` in app.json.

Build it faster with a template

Production-ready Expo React Native templates with the patterns from this post already wired up.

Browse templates