Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

File-based screens and routes

A mobile app has screens instead of pages. Expo Router builds navigation from files: every file in the app folder becomes a screen with its own address, like pages in Next.js. You do not write a list of routes by hand.

A new project: the blog

The second app starts in this chapter, a blog reader. Create the project and clean up the template:

npx create-expo-app@latest blog
cd blog
npm run reset-project
npx expo start

Expo Router is already installed and set up in the template.

A file is a route

FileScreen address
app/index.tsx/
app/about.tsx/about
app/posts/index.tsx/posts
app/posts/[id].tsx/posts/1, /posts/2 and so on
app/_layout.tsxnot a screen but a shared wrapper

Create the "About" screen file:

// app/about.tsx
import { Text, View } from 'react-native';
 
export default function About() {
  return (
    <View style={{ flex: 1, padding: 16 }}>
      <Text style={{ fontSize: 18 }}>A blog reader. Posts come from JSONPlaceholder.</Text>
    </View>
  );
}

A screen must have a default export (export default); that is how Expo Router knows what to show.

Linking to a screen

Navigation is done with the Link component from expo-router. Open the main screen:

// app/index.tsx
import { Link } from 'expo-router';
import { Text, View } from 'react-native';
 
export default function Index() {
  return (
    <View style={{ flex: 1, padding: 16, gap: 12 }}>
      <Text style={{ fontSize: 24 }}>Blog</Text>
      <Link href="/about" style={{ color: '#2563eb', fontSize: 18 }}>
        About
      </Link>
    </View>
  );
}

Tap the blue link and the "About" screen opens with about in the header and a back arrow. On Android you can also go back with the system back button. In a browser (the w key) the address changes to http://localhost:8081/about.

By default Link renders as text. To turn your own component into a link, use asChild: Link passes the press to the nested Pressable:

// app/index.tsx
import { Link } from 'expo-router';
import { Pressable, Text } from 'react-native';
 
<Link href="/about" asChild>
  <Pressable style={{ backgroundColor: '#2563eb', padding: 12, borderRadius: 8 }}>
    <Text style={{ color: '#fff' }}>About</Text>
  </Pressable>
</Link>

A blue button appears that leads to the same screen.

Common mistake

Exporting the screen component by name: export function About() instead of export default function About(). Expo Router cannot find what to show, prints a warning about the missing default export in the terminal, and the screen does not open. In files of the app folder the screen is always exported with export default. And if href has a typo, you get an "Unmatched Route" screen instead.

Thanks for reading!

Donate to the site — we work hard for you.