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, or start from the free Expo auth template. 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