Expo Router in React Native: Complete Handleiding voor Bestandsgebaseerde Navigatie

Uitgebreide handleiding over Expo Router voor React Native: bestandsgebaseerde navigatie, dynamische routes, tabnavigatie, modale schermen, getypeerde routes en routebeveiliging met praktische codevoorbeelden.

Bestandsgebaseerd navigatiesysteem van Expo Router voor mobiele React Native-applicaties

Expo Router heeft de navigatie in React Native-applicaties ingrijpend vereenvoudigd door bestandsgebaseerde routering te introduceren in het mobiele ecosysteem. Waar ontwikkelaars voorheen navigatiestacks handmatig moesten definiëren met complexe configuratieobjecten, volstaat het nu om bestanden aan te maken in de map app/. De mappenstructuur bepaalt automatisch de volledige navigatiehiërarchie van de applicatie. Dit principe, dat webframeworks zoals Next.js populair hebben gemaakt, biedt een consistente ontwikkelervaring voor iOS, Android en het web vanuit één enkele codebase. Met Expo SDK 55 en de nieuwste versie van Expo Router is bestandsgebaseerde navigatie de standaard geworden voor nieuwe Expo-projecten.

Snel aan de slag

Nieuwe Expo-projecten worden standaard met Expo Router geleverd. Met het commando npx create-expo-app@latest --template default@sdk-55 wordt een volledig geconfigureerd project gegenereerd met bestandsgebaseerde routering. Voor bestaande projecten volstaat het om het expo-router-pakket te installeren en het ingangspunt van de applicatie aan te passen.

Het principe van bestandsgebaseerde routering

Het kernidee achter Expo Router is rechtlijnig: elk bestand in de map app/ wordt automatisch een route in de applicatie. Er is geen centraal configuratiebestand nodig om routes te registreren. Een bestand op het pad app/settings.tsx resulteert in de route /settings, terwijl app/profile/edit.tsx overeenkomt met /profile/edit.

Ten opzichte van de traditionele configuratie met React Navigation levert deze aanpak drie wezenlijke voordelen op:

  • Geen configuratie vereist: routes ontstaan automatisch zodra het bestand wordt aangemaakt
  • Automatische deep linking: elk scherm beschikt over een unieke URL, wat delen en testen aanzienlijk vergemakkelijkt
  • Getypeerde navigatie: TypeScript kent de beschikbare routes al tijdens de compilatiefase

Het bestand index.tsx in de hoofdmap van app/ fungeert als startscherm van de applicatie:

app/index.tsxtypescript
import { View, Text, StyleSheet } from 'react-native'
import { Link } from 'expo-router'

export default function HomeScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Welcome</Text>
      {/* Link maps directly to file path */}
      <Link href="/settings" style={styles.link}>
        Open Settings
      </Link>
      <Link href="/profile/edit" style={styles.link}>
        Edit Profile
      </Link>
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', padding: 24 },
  title: { fontSize: 28, fontWeight: 'bold', marginBottom: 16 },
  link: { fontSize: 16, color: '#61DAFB', marginTop: 12 },
})

Het Link-component werkt op alle platformen. Op het web genereert het een standaard ankertag met correcte href-attributen, wat gunstig is voor zoekmachineoptimalisatie. Op iOS en Android activeert het een native stack-navigatie met de bijbehorende animaties en gebaren.

Projectstructuur en layoutbestanden

De organisatie van bestanden en mappen in een Expo Router-project volgt vaste conventies. Mappen representeren routesegmenten en _layout.tsx-bestanden bepalen hoe de onderliggende schermen worden weergegeven. Het root-layoutbestand fungeert als de primaire navigator en vervangt het traditionele App.tsx als ingangspunt.

Een representatieve projectstructuur ziet er als volgt uit:

text
app/
  _layout.tsx          # Root layout (Stack or custom)
  index.tsx            # Home screen (/)
  (tabs)/              # Tab group (parentheses = route group)
    _layout.tsx        # Tab navigator
    home.tsx           # /home tab
    search.tsx         # /search tab
    profile.tsx        # /profile tab
  settings/
    _layout.tsx        # Settings stack layout
    index.tsx          # /settings
    notifications.tsx  # /settings/notifications
    privacy.tsx        # /settings/privacy

Routegroepen -- mappen waarvan de naam tussen haakjes staat -- maken het mogelijk bestanden te organiseren zonder de URL-structuur te beïnvloeden. De map (tabs) in het voorbeeld hierboven creëert een tabnavigator, maar de URL's blijven /home, /search en /profile in plaats van /tabs/home.

Het root-layoutbestand definieert het navigatietype en de globale schermopties:

app/_layout.tsxtypescript
import { Stack } from 'expo-router'

export default function RootLayout() {
  return (
    <Stack
      screenOptions={{
        headerStyle: { backgroundColor: '#1a1a2e' },
        headerTintColor: '#ffffff',
        headerTitleStyle: { fontWeight: '600' },
      }}
    >
      <Stack.Screen name="index" options={{ title: 'Home' }} />
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen name="settings" options={{ title: 'Settings' }} />
    </Stack>
  )
}

Naast het definiëren van de navigatiestructuur dient de root layout ook als initialisatiepunt voor providers, het laden van lettertypen en andere globale configuratie die de gehele applicatie omvat.

Tabnavigatie configureren

Tabnavigatie vormt het meest gangbare navigatiepatroon in mobiele applicaties. In Expo Router wordt tabnavigatie geïmplementeerd door een _layout.tsx-bestand aan te maken binnen een routegroep en daarin het Tabs-component te gebruiken:

app/(tabs)/_layout.tsxtypescript
import { Tabs } from 'expo-router'
import { Ionicons } from '@expo/vector-icons'

export default function TabLayout() {
  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: '#61DAFB',
        tabBarInactiveTintColor: '#888',
        tabBarStyle: {
          backgroundColor: '#1a1a2e',
          borderTopColor: '#2d2d44',
        },
      }}
    >
      <Tabs.Screen
        name="home"
        options={{
          title: 'Home',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="home" size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="search"
        options={{
          title: 'Search',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="search" size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="profile"
        options={{
          title: 'Profile',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="person" size={size} color={color} />
          ),
        }}
      />
    </Tabs>
  )
}

Elke Tabs.Screen correspondeert met een bestand binnen de routegroep (tabs)/. De eigenschap name moet exact overeenkomen met de bestandsnaam zonder extensie. Expo Router behandelt iedere tab als een onafhankelijke navigatiestack, waardoor gebruikers binnen een tab naar subschermen kunnen navigeren zonder de status van andere tabs te verliezen.

Dynamische routes en routeparameters

Dynamische routes stellen ontwikkelaars in staat schermen te bouwen die reageren op variabele URL-segmenten. Door een bestandsnaam tussen vierkante haken te plaatsen, wordt een dynamisch routesegment aangemaakt. Het bestand [id].tsx vangt een enkel segment op, terwijl [...slug].tsx alle resterende segmenten vastlegt.

app/product/[id].tsxtypescript
import { View, Text, StyleSheet } from 'react-native'
import { useLocalSearchParams, Stack } from 'expo-router'

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

  return (
    <View style={styles.container}>
      <Stack.Screen options={{ title: `Product ${id}` }} />
      <Text style={styles.heading}>Product Details</Text>
      <Text style={styles.id}>ID: {id}</Text>
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24 },
  heading: { fontSize: 24, fontWeight: 'bold', marginBottom: 8 },
  id: { fontSize: 16, color: '#888' },
})

Bij het navigeren naar /product/42 wordt dit scherm weergegeven met id ingesteld op "42". De hook useLocalSearchParams biedt getypeerde toegang tot alle routeparameters en is lokaal aan het component, zodat elk scherm zijn eigen parameterwaarden behoudt in complexe navigatiescenario's.

Voor het opvangen van meervoudige URL-segmenten biedt Expo Router catch-all routes:

app/docs/[...slug].tsxtypescript
import { useLocalSearchParams } from 'expo-router'

export default function DocsScreen() {
  const { slug } = useLocalSearchParams<{ slug: string[] }>()

  return <DocViewer path={slug.join('/')} />
}

De route [...slug].tsx vangt een willekeurig aantal padsegmenten op na /docs/ en retourneert deze als een array van strings. Dit patroon is bijzonder geschikt voor documentatiesystemen, blogs of andere structuren met een hiërarchie van variabele diepte.

Klaar om je React Native gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Getypeerde routes voor compileertijdbeveiliging

Een van de krachtigste functies van Expo Router is de ondersteuning voor getypeerde routes. Wanneer deze functionaliteit is ingeschakeld, genereert Expo Router automatisch TypeScript-types voor alle routes in de applicatie. Hierdoor worden typefouten in URL's en ontbrekende routes al tijdens de compilatie opgespoord, nog voordat de applicatie wordt uitgevoerd.

De configuratie vindt plaats in het bestand app.json:

json
{
  "expo": {
    "experiments": {
      "typedRoutes": true
    }
  }
}

Na activering accepteren het Link-component en de methode router.push() uitsluitend routes die daadwerkelijk bestaan in het project:

app/checkout.tsxtypescript
import { router } from 'expo-router'

function handleCheckout(cartId: string) {
  router.push(`/product/${cartId}`)
}

In omvangrijke projecten met tientallen schermen en honderden navigatiekoppelingen is deze typeveiligheid van onschatbare waarde. TypeScript markeert ongeldige routes direct in de code-editor, waardoor fouten die anders pas tijdens het testen of in productie zouden optreden, vroegtijdig worden ondervangen. De gegenereerde types waarborgen bovendien de consistentie tussen parameternamen in de routedefinitie en het ontvangende component.

Modale schermen implementeren

Modals zijn schermen die over de bestaande inhoud heen worden weergegeven en worden doorgaans ingezet voor formulieren, bevestigingen of snelle detailweergaven. In Expo Router worden modale schermen geconfigureerd via de eigenschap presentation: 'modal' in de layout:

app/_layout.tsx (modal)typescript
import { Stack } from 'expo-router'

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="index" />
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen
        name="create-post"
        options={{
          presentation: 'modal',
          headerTitle: 'New Post',
        }}
      />
    </Stack>
  )
}

Het bijbehorende scherm gedraagt zich als een gewone route, inclusief ondersteuning voor deep linking en parameters:

app/create-post.tsxtypescript
import { View, TextInput, Button, StyleSheet } from 'react-native'
import { router } from 'expo-router'
import { useState } from 'react'

export default function CreatePostModal() {
  const [title, setTitle] = useState('')

  const handleSubmit = () => {
    router.back()
  }

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="Post title"
        value={title}
        onChangeText={setTitle}
      />
      <Button title="Publish" onPress={handleSubmit} />
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24 },
  input: {
    borderWidth: 1,
    borderColor: '#333',
    borderRadius: 8,
    padding: 12,
    fontSize: 16,
    marginBottom: 16,
  },
})

Navigeren naar /create-post activeert automatisch de modale presentatie. De aanroep router.back() sluit de modal en keert terug naar het vorige scherm. Op iOS functioneert het veeggebaar naar beneden om de modal te sluiten automatisch wanneer de presentatie als modal is ingesteld.

Programmatische navigatie met de Router-API

Naast het declaratieve Link-component biedt Expo Router een uitgebreide imperatieve API via het router-object. Deze API maakt navigatie mogelijk als reactie op gebruikersacties, resultaten van asynchrone bewerkingen of voorwaardelijke logica:

typescript
import { router } from 'expo-router'

router.push('/profile/settings')
router.replace('/login')
router.back()

router.push({
  pathname: '/product/[id]',
  params: { id: '42', source: 'recommendations' },
})

import { useRouter } from 'expo-router'

function BackButton() {
  const router = useRouter()
  return router.canGoBack() ? (
    <Button title="Back" onPress={() => router.back()} />
  ) : null
}

Het verschil tussen router.push en router.replace is van groot belang bij authenticatiestromen. De methode push voegt een nieuw item toe aan de navigatiegeschiedenis, waardoor de gebruiker kan terugkeren via de terugknop. De methode replace vervangt het huidige scherm in de navigatiestack en is ideaal voor scenario's waarin terugnavigeren ongewenst is, zoals het omleiden naar het dashboard na een geslaagde aanmelding.

Navigatie versus omleiding

router.replace() vervangt het huidige item in de navigatiegeschiedenis. Voor authenticatieguards die niet-ingelogde gebruikers moeten omleiden, verdient het <Redirect href="/login" />-component de voorkeur boven router.replace(). Dit component wordt geactiveerd tijdens de renderfase en functioneert correct met server-side rendering op het web.

Middleware en routebeveiliging

Het beschermen van routes tegen ongeautoriseerde toegang is een essentieel onderdeel van productieapplicaties. Expo Router biedt hiervoor twee complementaire mechanismen: server-middleware voor webomgevingen en authenticatielayouts die op alle platformen werken.

Server-middleware onderschept verzoeken voordat de rendering plaatsvindt en is uitsluitend beschikbaar in webcontexten:

app/+middleware.tstypescript
import { type MiddlewareRequest } from 'expo-router/server'

export function middleware(request: MiddlewareRequest) {
  const { pathname } = request.nextUrl

  const protectedPaths = ['/dashboard', '/settings', '/profile']
  const isProtected = protectedPaths.some(p => pathname.startsWith(p))

  if (isProtected) {
    const token = request.cookies.get('session')
    if (!token) {
      return Response.redirect(new URL('/login', request.url))
    }
  }

  return undefined
}

Voor een platformonafhankelijke aanpak die zowel op het web als op mobiele apparaten werkt, vormt een authenticatielayout het aanbevolen patroon. Deze layout controleert de sessie van de gebruiker en leidt bij afwezigheid om naar het inlogscherm:

app/(authenticated)/_layout.tsxtypescript
import { Redirect, Stack } from 'expo-router'
import { useAuth } from '@/hooks/useAuth'

export default function AuthenticatedLayout() {
  const { isLoggedIn, isLoading } = useAuth()

  if (isLoading) return null
  if (!isLoggedIn) return <Redirect href="/login" />

  return <Stack />
}

Alle routes binnen de groep (authenticated)/ doorlopen automatisch de authenticatiecontrole. Het Redirect-component leidt niet-ingelogde gebruikers om naar het inlogscherm, terwijl de null-return tijdens het laden voorkomt dat beschermde inhoud kort zichtbaar is.

Platformspecifieke overwegingen

Server-middleware (+middleware.ts) functioneert uitsluitend op het web met server-side rendering. Voor routebeveiliging op iOS en Android zijn authenticatielayouts met client-side controle de aangewezen oplossing. Multiplatformapplicaties dienen beide strategieën te combineren om volledige dekking te garanderen.

Conclusie

Expo Router heeft zich gevestigd als de standaardoplossing voor navigatie in het React Native-ecosysteem. Door bestandsgebaseerde routering centraal te stellen, elimineert het framework de complexiteit van handmatige navigatieconfiguratie en brengt het de mobiele ontwikkelervaring in lijn met de conventies van moderne webframeworks. De kernpunten uit deze handleiding:

  • Bestandsgebaseerde routering vertaalt de mappenstructuur automatisch naar routes, waardoor handmatige configuratie overbodig wordt
  • Layoutbestanden (_layout.tsx) definiëren het navigatietype (Stack, Tabs, Drawer) en bepalen de weergave van onderliggende schermen
  • Routegroepen met haakjes zoals (tabs) organiseren bestanden logisch zonder de URL-structuur te beïnvloeden
  • Dynamische routes met [id].tsx en catch-all routes met [...slug].tsx bieden flexibele URL-parameters met volledige TypeScript-ondersteuning
  • Getypeerde routes detecteren ongeldige navigatiekoppelingen al tijdens de compilatie wanneer ingeschakeld via app.json
  • Modale schermen worden geïmplementeerd via presentatieopties in de layout en ondersteunen deep linking als volwaardige routes
  • De router-API biedt programmatische navigatie met push, replace, back en canGoBack voor imperatieve navigatiescenario's
  • Routebeveiliging combineert server-middleware (web) met authenticatielayouts (cross-platform) voor toegangscontrole op alle platformen

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Tags

#react-native
#expo
#expo-router
#navigation
#mobile-development
#tutorial

Delen

Gerelateerde artikelen