Expo Router pour React Native : Guide Complet de la Navigation par Fichiers
Maîtriser Expo Router pour React Native avec ce guide couvrant le routage par fichiers, les layouts, les routes dynamiques, la navigation typée et les patterns avancés comme les modales et les onglets.

Expo Router apporte le routage basé sur les fichiers à React Native, remplaçant la configuration manuelle de navigation par une approche conventionnelle inspirée de Next.js. Avec le SDK Expo 57 et Expo Router, la construction de la navigation multiplateforme pour Android, iOS et le web ne nécessite rien de plus que la création de fichiers dans le bon répertoire.
Les nouveaux projets Expo intègrent Expo Router par défaut. La commande npx create-expo-app@latest --template default génère un projet préconfiguré avec le routage par fichiers. Pour les projets existants, il suffit d'installer le package expo-router et de mettre à jour le point d'entrée de l'application.
Fonctionnement du routage par fichiers dans Expo Router
Chaque fichier placé dans le répertoire app devient automatiquement une route. Le chemin du fichier correspond directement au chemin de l'URL, ce qui élimine toute nécessité d'un fichier de configuration centralisé. Un fichier app/settings.tsx crée la route /settings, tandis que app/profile/edit.tsx correspond à /profile/edit.
Cette approche offre trois avantages majeurs par rapport à la configuration traditionnelle avec React Navigation :
- Zéro configuration : les routes existent dès la création du fichier
- Deep linking automatique : chaque écran dispose d'une URL, facilitant le partage et les tests
- Navigation typée : TypeScript connaît les routes existantes dès la compilation
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 },
})Le composant Link gère la navigation sur toutes les plateformes. Sur le web, il génère une balise ancre avec les attributs href appropriés pour le référencement. Sur les plateformes natives, il déclenche une navigation de type stack.
Structure du projet et fichiers de layout
Expo Router utilise des fichiers _layout.tsx pour définir les conteneurs de navigation. Chaque répertoire peut contenir son propre layout, créant ainsi des hiérarchies de navigation imbriquées. Le layout racine enveloppe l'ensemble de l'application, tandis que les layouts imbriqués contrôlent des sections spécifiques.
Voici une structure de projet typique :
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/privacyLes groupes de routes, des répertoires entourés de parenthèses, organisent les fichiers sans affecter l'URL. Le répertoire (tabs) ci-dessus crée un navigateur par onglets, mais les URL restent /home, /search et /profile plutôt que /tabs/home.
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>
)
}Le layout racine sert également de point d'initialisation pour le chargement des polices, la configuration des providers et les réglages globaux.
Construction d'une navigation par onglets
La navigation par onglets nécessite un fichier _layout.tsx à l'intérieur d'un groupe de routes. Expo Router propose à la fois NativeTabs pour des expériences natives spécifiques à chaque plateforme et le composant standard Tabs pour la plupart des cas d'utilisation. Depuis le SDK 56, Native Tabs supporte la propriété disabled pour empêcher la sélection d'un onglet tout en le gardant visible.
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>
)
}Chaque fichier d'écran d'onglet exporte un composant React standard. L'icône, le libellé et le badge de la barre d'onglets se configurent via la prop options du layout.
Routes dynamiques et paramètres de route
Les segments dynamiques utilisent des crochets dans le nom du fichier. Un fichier nommé [id].tsx correspond à un segment unique, tandis que [...slug].tsx capture tous les segments restants du chemin.
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' },
})Accéder à /product/42 affiche cet écran avec la variable id définie sur "42". Le hook useLocalSearchParams fournit un accès typé à l'ensemble des paramètres de route.
Pour les routes attrape-tout, [...slug].tsx capture des segments de chemin entiers :
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('/')} />
}Prêt à réussir tes entretiens React Native ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Typage des routes pour une sécurité à la compilation
Expo Router génère automatiquement les types de routes lorsque la fonctionnalité typed routes est activée. Cette vérification détecte les liens cassés à la compilation plutôt qu'à l'exécution.
Activation dans le fichier app.json :
{
"expo": {
"experiments": {
"typedRoutes": true
}
}
}Une fois activé, la prop href du composant Link et l'argument de router.push() n'acceptent que des chaînes correspondant à des routes valides :
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')
}Le typage des routes fonctionne particulièrement bien avec useLocalSearchParams. Les types générés garantissent la correspondance entre les noms de paramètres dans la définition de la route et ceux utilisés par le composant consommateur, prévenant ainsi les bugs subtils qui ne se manifestent qu'à la navigation vers un écran spécifique.
Écrans modaux et options de présentation
Les modales dans Expo Router sont des écrans classiques configurés avec presentation: 'modal' dans le layout. Cette approche respecte la convention du routage par fichiers : une modale reste simplement une route comme les autres.
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>
)
}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,
},
})Accéder à /create-post déclenche la présentation modale. L'appel à router.back() la ferme et ramène l'utilisateur à l'écran précédent dans la pile de navigation.
Navigation programmatique et API du routeur
Au-delà du composant Link, Expo Router fournit une API impérative via l'objet router. Celle-ci permet de gérer la navigation déclenchée par la logique métier plutôt que par une interaction directe de l'utilisateur.
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
}La distinction entre router.push et router.replace est essentielle dans les flux d'authentification en React Native. Après une connexion réussie, router.replace('/dashboard') empêche l'utilisateur de revenir à l'écran de connexion via le bouton retour.
router.replace() remplace l'entrée courante dans l'historique de navigation. Pour les gardes d'authentification qui redirigent les utilisateurs non connectés, il est préférable d'utiliser <Redirect href="/login" /> dans le rendu du composant. Ce dernier se déclenche pendant la phase de rendu et fonctionne correctement avec le rendu côté serveur sur le web.
Middleware et protection des routes
Expo Router fournit un middleware serveur pour la logique appliquée au niveau des routes. Le fichier +middleware.ts intercepte les requêtes avant qu'elles n'atteignent le composant de la route, permettant ainsi des vérifications d'authentification, des redirections et la manipulation des en-têtes HTTP.
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
}Sur les plateformes natives, le middleware serveur ne s'exécute pas. La protection des routes repose alors sur des gardes côté client. Le pattern le plus courant consiste à envelopper les layouts protégés d'une vérification d'authentification :
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 />
}Le middleware serveur ne s'exécute que sur le web avec le rendu côté serveur. Pour les applications natives, il est impératif d'implémenter des gardes côté client dans les composants de layout. Combiner les deux approches garantit une protection cohérente sur l'ensemble des plateformes.
Migration SDK 56 : fork de React Navigation
À partir du SDK Expo 56, Expo Router ne dépend plus des packages @react-navigation/*. Si votre projet importe directement depuis React Navigation, les imports doivent être mis à jour :
| Package original | Nouvel emplacement |
|---|---|
@react-navigation/native | expo-router/react-navigation |
@react-navigation/stack | expo-router/js-stack |
@react-navigation/bottom-tabs | expo-router/js-tabs |
Exécutez le codemod pour mettre à jour les imports automatiquement :
npx expo-codemod sdk-56-expo-router-react-navigation-replace srcL'API à l'exécution reste inchangée. Seuls les spécificateurs de modules ont été déplacés. Les bibliothèques tierces qui importent encore depuis @react-navigation/core fonctionnent automatiquement car Expo CLI réécrit ces imports lors du bundling.
Sources
- Documentation Expo Router : guide officiel et référence API
- Changelog SDK Expo 57 : notes de version de la release stable actuelle
- Article de blog Expo Router v56 : explication du fork de React Navigation
- Guide de migration SDK 55 vers 56 : étapes de migration des imports
Ce que les recruteurs attendent sur Expo Router
- Expo Router associe automatiquement chaque fichier du répertoire
appà une route, éliminant la configuration manuelle de navigation - Les layouts définis via les fichiers
_layout.tsxcréent des hiérarchies de navigation pour les stacks, onglets et drawers - Les routes dynamiques avec
[param].tsxet les routes attrape-tout avec[...slug].tsxgèrent la navigation paramétrée avec un support TypeScript complet - Le typage des routes détecte les liens de navigation cassés à la compilation lorsque la fonctionnalité est activée dans
app.json - Les écrans modaux, la navigation programmatique via
router.push/replace/backet le middleware serveur complètent la boîte à outils du routage - Les groupes de routes par répertoires entre parenthèses organisent le code sans affecter les URL
- Les gardes côté client dans les layouts gèrent l'authentification sur le natif, tandis que le middleware serveur couvre le web
- Depuis le SDK 56, les imports depuis
@react-navigation/*doivent être mis à jour vers les équivalentsexpo-router/*
Pour approfondir l'architecture React Native et la préparation aux entretiens, consultez le module sur les modules natifs.
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Tu saurais repérer le bug en React Native ?
Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Écrit par
Anthony Fillion-MailletFondateur de SharpSkill
Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.
Mis à jour le 6 octobre 2026
Tags
Partager
Articles similaires

Développement d'Applications React Native en 2026 : Guide Complet et Questions d'Entretien
Maîtrisez le développement d'applications React Native en 2026 avec la Nouvelle Architecture, Hermes V1 et Fabric. Exemples de code pratiques et questions d'entretien techniques.

React Native 0.87 et SwiftPM en 2026 : Build iOS Moderne et Questions d'Entretien
React Native 0.87 introduit le support expérimental de Swift Package Manager, éliminant CocoaPods des builds iOS. Ce guide couvre la configuration SwiftPM, l'API TypeScript Stricte, les optimisations Metro 0.87, et les questions d'entretien pour développeurs mobile seniors.

Hermes V1 dans React Native 0.84 : Performance, Bytecode Précompilé et Questions d'Entretien
Analyse approfondie des optimisations de performance d'Hermes V1 dans React Native 0.84 : précompilation du bytecode, garbage collector Hades, gestion mémoire et questions techniques pour développeurs mobiles.