Cours React Native · Module 3: Navigation

Expo Router - La navigation moderne

4 min de lecture ·
Dans cette leçon10

Expo Router est un nouveau système de navigation révolutionnaire basé sur les fichiers, semblable à Next.js pour React Native !

Pourquoi Expo Router ?

  • Routage basé sur les fichiers - la structure des dossiers = la structure de la navigation
  • Deep Linking automatique - aucune configuration supplémentaire nécessaire
  • Routes typées - prise en charge complète de TypeScript
  • Support du web - fonctionne sur le web comme Next.js
  • Lazy loading - les écrans sont chargés à la demande

Installation

# Nouveau projet avec Expo Router
npx create-expo-app@latest --template tabs

# Ou l'ajouter à un projet existant
npx expo install expo-router expo-linking expo-constants

Configuration

// app.json
{
  "expo": {
    "scheme": "atlantis",
    "web": {
      "bundler": "metro"
    },
    "plugins": ["expo-router"]
  }
}
// package.json
{
  "main": "expo-router/entry"
}

Structure des fichiers = routage

app/
├── _layout.tsx          # Layout racine
├── index.tsx            # / (page d'accueil)
├── about.tsx            # /about
├── settings/
│   ├── _layout.tsx      # Layout pour /settings/*
│   ├── index.tsx        # /settings
│   └── profile.tsx      # /settings/profile
├── user/
│   └── [id].tsx         # /user/123 (paramètre dynamique)
├── (tabs)/
│   ├── _layout.tsx      # Tab Navigator
│   ├── home.tsx         # Onglet Home
│   └── search.tsx       # Onglet Search
└── +not-found.tsx       # Page 404

Layout racine

// app/_layout.tsx
import { Stack } from 'expo-router';
import { ThemeProvider } from '@/providers/ThemeProvider';

export default function RootLayout() {
  return (
    <ThemeProvider>
      <Stack
        screenOptions={{
          headerStyle: { backgroundColor: '#0a2e4a' },
          headerTintColor: '#fff',
        }}
      >
        <Stack.Screen name="index" options={{ title: 'Atlantis' }} />
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen name="user/[id]" options={{ title: 'Profil' }} />
      </Stack>
    </ThemeProvider>
  );
}

Page d'accueil

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

export default function Home() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Bienvenue à Atlantis !</Text>

      {/* Link en tant que composant */}
      <Link href="/about" style={styles.link}>
        À propos de l'application
      </Link>

      {/* Link avec des paramètres */}
      <Link
        href={{
          pathname: '/user/[id]',
          params: { id: '123' }
        }}
        style={styles.link}
      >
        Mon profil
      </Link>

      {/* Link vers le Tab Navigator */}
      <Link href="/(tabs)/home" style={styles.link}>
        Aller aux onglets
      </Link>
    </View>
  );
}

Routes dynamiques

// app/user/[id].tsx
import { useLocalSearchParams, useGlobalSearchParams, Stack } from 'expo-router';

export default function UserProfile() {
  // Récupérer le paramètre depuis l'URL
  const { id } = useLocalSearchParams<{ id: string }>();

  return (
    <View>
      {/* Titre dynamique */}
      <Stack.Screen options={{ title: `Utilisateur #${id}` }} />

      <Text>Profil de l'utilisateur : {id}</Text>
    </View>
  );
}

Tab Navigator

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

export default function TabLayout() {
  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: '#00d4ff',
        tabBarStyle: { backgroundColor: '#0a2e4a' },
      }}
    >
      <Tabs.Screen
        name="home"
        options={{
          title: 'Accueil',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="home" size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="search"
        options={{
          title: 'Recherche',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="search" size={size} color={color} />
          ),
        }}
      />
    </Tabs>
  );
}
import { router } from 'expo-router';

function NavigationButtons() {
  return (
    <View>
      {/* Naviguer vers un chemin */}
      <Pressable onPress={() => router.push('/about')}>
        <Text>À propos de l'application</Text>
      </Pressable>

      {/* Naviguer avec des paramètres */}
      <Pressable onPress={() => router.push({
        pathname: '/user/[id]',
        params: { id: '456' }
      })}>
        <Text>Profil 456</Text>
      </Pressable>

      {/* Replace (sans possibilité de revenir en arrière) */}
      <Pressable onPress={() => router.replace('/login')}>
        <Text>Se déconnecter</Text>
      </Pressable>

      {/* Revenir en arrière */}
      <Pressable onPress={() => router.back()}>
        <Text>Retour</Text>
      </Pressable>

      {/* Vérifier si l'on peut revenir en arrière */}
      {router.canGoBack() && (
        <Pressable onPress={() => router.back()}>
          <Text>Retour</Text>
        </Pressable>
      )}
    </View>
  );
}

Groupes (masqués de l'URL)

// Les groupes entre parenthèses n'apparaissent pas dans l'URL
// app/(auth)/login.tsx → /login
// app/(tabs)/home.tsx → /home (et non /(tabs)/home)

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

export default function AuthLayout() {
  return (
    <Stack screenOptions={{ headerShown: false }}>
      <Stack.Screen name="login" />
      <Stack.Screen name="register" />
    </Stack>
  );
}

Expo Router est l'avenir de la navigation dans React Native. Il simplifie énormément le routage et gère automatiquement le Deep Linking !

Code de cette leçon : App.tsx
// Fichier : app/_layout.tsx (Expo Router - routage basé sur les fichiers)
import React, { useState } from 'react';
import { Stack, Redirect } from 'expo-router';

// Hook simple simulant l'état de connexion de l'utilisateur
function useAuth() {
  const [isSignedIn] = useState(false);
  return { isSignedIn };
}

export default function RootLayout() {
  const { isSignedIn } = useAuth();

  // TODO : si l'utilisateur N'est PAS connecté, redirige vers l'écran de connexion :
  // if (!isSignedIn) {
  //   return <Redirect href="/login" />;
  // }

  // TODO : retourne un <Stack> qui masque l'en-tête pour le groupe (tabs) :
  // <Stack>
  //   <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
  // </Stack>

  return null;
}

Entrées de la documentation utilisées dans cette leçon

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Qu'est-ce que le routage basé sur les fichiers dans Expo Router ?

  2. 2. Que signifie le nom de fichier [id].tsx dans Expo Router ?

Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Tri vertical

    Range l'imbrication des layouts en partant de la racine :

  • Éditeur de code

    Complète le code de _layout.tsx.

  • Tri horizontal

    Range la récupération des paramètres dans Expo Router :

Articles utiles