Expo Router in React Native: Vollständiger Leitfaden zur dateibasierten Navigation

Expo Router für React Native: dateibasiertes Routing, Layouts, dynamische Routen, typisierte Navigation, Tabs, Modals, Middleware und SDK 56 Migration.

Dateibasiertes Navigationssystem mit Expo Router in React Native

Expo Router bringt dateibasiertes Routing nach React Native und ersetzt die manuelle Navigationskonfiguration durch einen konventionsgetriebenen Ansatz, der von Next.js inspiriert ist. Seit dem Expo SDK 57 und Expo Router erfordert die Entwicklung plattformübergreifender Navigation für Android, iOS und das Web nichts weiter als das Erstellen von Dateien im richtigen Verzeichnis.

Schnellstart

Neue Expo-Projekte werden mit vorkonfiguriertem Expo Router ausgeliefert. Mit dem Befehl npx create-expo-app@latest --template default entsteht ein Projekt mit dateibasiertem Routing direkt einsatzbereit. Für bestehende Projekte genügt die Installation des Pakets expo-router sowie die Anpassung des Einstiegspunkts in der Projektkonfiguration.

So funktioniert dateibasiertes Routing in Expo Router

Jede Datei innerhalb des app-Verzeichnisses wird automatisch zu einer Route. Der Dateipfad entspricht direkt dem URL-Pfad, sodass keine zentrale Navigationskonfiguration erforderlich ist. Eine Datei unter app/settings.tsx erzeugt die Route /settings, während app/profile/edit.tsx auf /profile/edit abgebildet wird.

Dieser Ansatz bietet drei wesentliche Vorteile gegenüber dem traditionellen React-Navigation-Setup:

  • Keine Konfiguration: Routen existieren, sobald eine Datei erstellt wird
  • Automatisches Deep Linking: Jeder Bildschirm erhält eine URL, was das Teilen und Testen ermöglicht
  • Typsichere Navigation: TypeScript kennt die vorhandenen Routen bereits zur Kompilierzeit
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 },
})

Die Link-Komponente übernimmt die Navigation auf allen Plattformen. Im Web rendert sie ein Anchor-Tag mit korrekten href-Attributen für SEO. Auf nativen Plattformen löst sie eine Stack-basierte Navigation aus.

Projektstruktur und Layout-Dateien

Expo Router verwendet _layout.tsx-Dateien zur Definition von Navigationscontainern. Jedes Verzeichnis kann sein eigenes Layout besitzen, wodurch verschachtelte Navigationshierarchien entstehen. Das Root-Layout umschließt die gesamte App, während verschachtelte Layouts spezifische Bereiche steuern.

Eine typische Projektstruktur sieht folgendermaßen aus:

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

Routengruppen, also Verzeichnisse in runden Klammern, organisieren Dateien ohne Auswirkung auf die URL. Das Verzeichnis (tabs) oben erstellt einen Tab-Navigator, aber die URLs bleiben /home, /search und /profile statt /tabs/home.

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

Das Root-Layout dient auch als Ort zum Laden von Schriften, Initialisieren von Providern und Konfigurieren globaler Einstellungen.

Tab-Navigation mit Expo Router erstellen

Tab-Navigation erfordert eine _layout.tsx-Datei innerhalb einer Routengruppe. Expo Router bietet sowohl NativeTabs für plattformspezifische Tab-Erlebnisse als auch die Standard-Tabs-Komponente für die meisten Anwendungsfälle. Seit SDK 56 unterstützt NativeTabs die Eigenschaft disabled, um die Tab-Auswahl zu verhindern, während die Tabs sichtbar bleiben.

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

Jede Tab-Bildschirmdatei exportiert eine Standard-React-Komponente. Das Tab-Bar-Icon, die Beschriftung und das Badge werden über die options-Prop im Layout konfiguriert.

Dynamische Routen und Routenparameter

Dynamische Segmente verwenden eckige Klammern im Dateinamen. Eine Datei namens [id].tsx erfasst ein einzelnes Segment, während [...slug].tsx alle verbleibenden Segmente auffängt.

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

export default function ProductScreen() {
  // Extract the dynamic parameter from the URL
  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' },
})

Die Navigation zu /product/42 rendert diesen Bildschirm mit id auf "42" gesetzt. Der Hook useLocalSearchParams bietet typisierten Zugriff auf alle Routenparameter.

Für Catch-all-Routen erfasst [...slug].tsx vollständige Pfadsegmente:

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

export default function DocsScreen() {
  // /docs/getting-started/installation → slug = ['getting-started', 'installation']
  const { slug } = useLocalSearchParams<{ slug: string[] }>()

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

Bereit für deine React Native-Interviews?

Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.

Typisierte Routen für Kompilierzeit-Sicherheit

Expo Router generiert automatisch Routentypen, wenn typed routes aktiviert ist. Dies erkennt defekte Links zur Kompilierzeit statt zur Laufzeit.

Typisierte Routen in app.json aktivieren:

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

Nach der Aktivierung akzeptiert die href-Prop auf Link und das Argument für router.push() nur gültige Routenstrings:

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

function handleCheckout(cartId: string) {
  // TypeScript validates this route exists
  router.push(\`/product/\${cartId}\`)

  // This would cause a compile error if /nonexistent doesn't exist
  // router.push('/nonexistent')
}

Typisierte Routen harmonieren gut mit useLocalSearchParams. Die generierten Typen stellen sicher, dass Parameternamen zwischen der Routendefinition und der konsumierenden Komponente übereinstimmen, wodurch subtile Bugs vermieden werden, die sonst erst beim Navigieren zu einem bestimmten Bildschirm auftreten würden.

Modale Bildschirme und Präsentationsoptionen

Modals in Expo Router sind reguläre Bildschirme, die mit presentation: 'modal' im Layout konfiguriert werden. Dieser Ansatz behält die dateibasierte Konvention bei: Ein Modal ist einfach eine weitere Route.

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

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="index" />
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      {/* Modal screen slides up from the bottom */}
      <Stack.Screen
        name="create-post"
        options={{
          presentation: 'modal',
          headerTitle: 'New Post',
        }}
      />
    </Stack>
  )
}
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 = () => {
    // Submit logic here
    router.back() // Dismiss the modal
  }

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

Die Navigation zu /create-post löst die modale Präsentation aus. Der Aufruf von router.back() schließt das Modal und kehrt zum vorherigen Bildschirm im Stack zurück.

Programmatische Navigation und die Router-API

Über die Link-Komponente hinaus bietet Expo Router eine imperative API über das router-Objekt. Diese behandelt Navigation, die durch Geschäftslogik statt durch Benutzereingaben ausgelöst wird.

typescript
import { router } from 'expo-router'

// Push a new screen onto the stack
router.push('/profile/settings')

// Replace the current screen (no back button)
router.replace('/login')

// Go back to the previous screen
router.back()

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

// Check if going back is possible
import { useRouter } from 'expo-router'

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

Die Unterscheidung zwischen router.push und router.replace ist wichtig für Authentifizierungsabläufe in React Native. Nach einer erfolgreichen Anmeldung verhindert router.replace('/dashboard'), dass der Benutzer zum Login-Bildschirm zurücknavigieren kann.

Navigation vs. Redirect

router.replace() ersetzt den aktuellen Eintrag im Navigationsverlauf. Für Authentifizierungswächter, die nicht authentifizierte Benutzer umleiten, sollte <Redirect href="/login" /> innerhalb des Komponenten-Renders verwendet werden: Es wird während der Render-Phase ausgelöst und funktioniert korrekt mit serverseitigem Rendering im Web.

Middleware und Routenschutz

Expo Router bietet Server-Middleware für Logik auf Routenebene. Die Datei +middleware.ts fängt Anfragen ab, bevor sie die Routenkomponente erreichen, und ermöglicht Authentifizierungsprüfungen, Weiterleitungen und Header-Manipulation.

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

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

  // Protect dashboard routes
  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 // Continue to route
}

Auf nativen Plattformen, auf denen Server-Middleware nicht läuft, basiert der Routenschutz auf clientseitigen Wächtern. Ein gängiges Muster umhüllt geschützte Layouts mit einer Authentifizierungsprüfung:

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 />
}
Plattformbewusstsein

Server-Middleware läuft nur im Web mit serverseitigem Rendering. Für native Apps sollten clientseitige Routenwächter in Layout-Komponenten implementiert werden. Die Kombination beider Ansätze gewährleistet konsistenten Schutz auf allen Plattformen.

SDK 56 Migration: React Navigation Fork

Ab dem Expo SDK 56 hängt Expo Router nicht mehr von den @react-navigation/*-Paketen ab. Falls das Projekt direkt aus React Navigation importiert, müssen die Imports aktualisiert werden:

Ursprüngliches PaketNeuer Speicherort
@react-navigation/nativeexpo-router/react-navigation
@react-navigation/stackexpo-router/js-stack
@react-navigation/bottom-tabsexpo-router/js-tabs

Der Codemod aktualisiert die Imports automatisch:

bash
npx expo-codemod sdk-56-expo-router-react-navigation-replace src

Die Laufzeit-API bleibt unverändert. Nur die Modulspezifizierer wurden verschoben. Drittanbieter-Bibliotheken, die weiterhin aus @react-navigation/core importieren, funktionieren automatisch, da Expo CLI diese Imports während des Bundlings umschreibt.

Quellen

Was Interviewer über Expo Router erwarten

  • Expo Router bildet jede Datei im app-Verzeichnis automatisch auf eine Route ab und eliminiert die manuelle Navigationskonfiguration
  • Layouts, definiert durch _layout.tsx-Dateien, erstellen Navigationshierarchien für Stacks, Tabs und Drawers
  • Dynamische Routen mit [param].tsx und Catch-all-Routen mit [...slug].tsx handhaben parametrisierte Navigation mit vollständiger TypeScript-Unterstützung
  • Typisierte Routen erkennen defekte Navigationslinks zur Kompilierzeit, wenn sie in app.json aktiviert sind
  • Modale Bildschirme, programmatische Navigation über router.push/replace/back und Server-Middleware vervollständigen das Routing-Toolkit
  • Routengruppen mit Klammern in Verzeichnisnamen organisieren Code ohne Auswirkung auf URLs
  • Clientseitige Routenwächter in Layouts übernehmen die Authentifizierung auf nativen Plattformen, während Server-Middleware das Web abdeckt
  • Seit SDK 56 müssen Imports aus @react-navigation/* auf expo-router/*-Äquivalente aktualisiert werden

Für mehr zu React Native Architektur und Interviewvorbereitung siehe das Native-Module-Modul.

Fang an zu üben!

Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.

Tägliche Challenge

Findest du den Bug in React Native?

Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Anthony Fillion-Maillet

Geschrieben von

Anthony Fillion-Maillet

Gründer von SharpSkill

Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.

Aktualisiert am 6. Oktober 2026

Tags

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

Teilen

Verwandte Artikel