React Native avec Expo, c'est notre stack de référence pour livrer des apps iOS et Android à partir d'une seule base de code. À force de projets, certains choix se sont imposés et se retrouvent systématiquement dans notre base de code. Les 7 patterns ci-dessous reviennent sur chacun de nos projets en production.
- Expo Router par défaut : routing par fichiers, deep links et Universal Links gérés par la structure.
- Offline-first avec MMKV + React Query : cache local rapide, resynchronisation automatique.
- Error Boundaries branchées sur Sentry : aucune erreur silencieuse en production.
- EAS Update pour les correctifs : livraison OTA en quelques minutes, sans review des stores.
1. Navigation : Expo Router par défaut#
Expo Router apporte le file-based routing à React Native. Même logique que Next.js : les devs web s'y retrouvent immédiatement.
app/
├── (tabs)/
│ ├── index.tsx // Onglet Home
│ ├── profile.tsx // Onglet Profil
│ └── _layout.tsx // Tab bar config
├── modal.tsx // Modale (full-screen)
└── _layout.tsx // Root layout (providers)
2. Deep Links avec Expo Router#
// app/product/[id].tsx
import { useLocalSearchParams } from 'expo-router'
export default function ProductScreen() {
const { id } = useLocalSearchParams<{ id: string }>()
// Accessible via myapp://product/123
// ET via https://monsite.fr/product/123 (Universal Links)
}3. Push notifications avec Expo Notifications#
La partie technique ci-dessous ne suffit pas : le timing de la demande de permission fait tout le taux d'opt-in. On a détaillé les règles de notifications depuis iOS 17.
import * as Notifications from 'expo-notifications'
import * as Device from 'expo-device'
async function registerForPushNotifications() {
if (!Device.isDevice) return null
const { status } = await Notifications.requestPermissionsAsync()
if (status !== 'granted') return null
const token = await Notifications.getExpoPushTokenAsync({
projectId: Constants.expoConfig?.extra?.eas?.projectId,
})
return token.data
}4. Offline-first avec MMKV + React Query#
import { MMKV } from 'react-native-mmkv'
import { QueryClient } from '@tanstack/react-query'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'
const storage = new MMKV()
const persister = createSyncStoragePersister({
storage: {
getItem: (key) => storage.getString(key) ?? null,
setItem: (key, value) => storage.set(key, value),
removeItem: (key) => storage.delete(key),
},
})5. Performance : FlatList optimisée#
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ItemCard item={item} />}
// Ces 3 props sont critiques pour les listes longues
removeClippedSubviews
maxToRenderPerBatch={10}
windowSize={10}
// Evite les re-renders inutiles
getItemLayout={(_, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
/>6. Gestion des erreurs avec Error Boundaries#
import * as Sentry from '@sentry/react-native'
export function AppErrorBoundary({ children }: { children: React.ReactNode }) {
return (
<Sentry.ErrorBoundary
fallback={({ error, resetError }) => (
<View style={styles.container}>
<Text>Une erreur est survenue</Text>
<Button title='Réessayer' onPress={resetError} />
</View>
)}
>
{children}
</Sentry.ErrorBoundary>
)
}7. OTA updates avec EAS Update#
# Déploiement sans passer par l'App Store
eas update --channel production --message "Fix crash login"Nos apps sont configurées pour checker les updates au démarrage. Les correctifs critiques arrivent chez les utilisateurs en moins de 10 minutes, sans attendre la review Apple.
Ce qu'on évite systématiquement#
expo-avpour la vidéo (préférerreact-native-video)- Nested navigators trop profonds (max 3 niveaux)
- useState pour l'état global (Zustand ou Jotai)
- AsyncStorage (trop lent, MMKV est bien plus rapide)
Tous ces patterns s'écrivent en TypeScript strict, sans exception. Si vous vous demandez encore si React Native est le bon choix pour votre projet, on a posé notre raisonnement complet, et le détail de l'offre est sur la page application mobile.
Questions fréquentes
Pourquoi utiliser Expo Router plutôt que React Navigation seul ?
Expo Router apporte le routing par fichiers à React Native, sur le même modèle que Next.js : les développeurs web s'y retrouvent immédiatement, et les deep links comme les Universal Links sont gérés par la structure des fichiers elle-même.
Comment faire fonctionner une app React Native hors ligne ?
Le duo MMKV + React Query avec un persister couvre la majorité des besoins : les données sont mises en cache localement et resynchronisées au retour du réseau. MMKV est nettement plus rapide qu'AsyncStorage pour ce rôle.
Qu'est-ce qu'une mise à jour OTA avec EAS Update ?
Une mise à jour over-the-air livre le code JavaScript directement aux utilisateurs sans repasser par la validation des stores. Les correctifs non natifs arrivent en quelques minutes au lieu de quelques jours.
Quelles erreurs éviter sur une app React Native ?
Les plus coûteuses : AsyncStorage pour le stockage critique (MMKV est bien plus rapide), useState pour l'état global (préférer Zustand ou Jotai), et des navigateurs imbriqués trop profonds qui rendent la navigation imprévisible.