Développement Web

React Native : développer des applications mobiles multi-plateformes

9 دقائق للقراءة

React Native est devenu en 2026 la voie la plus pragmatique pour livrer une application mobile sur Android et iOS à partir d’une base de code unique. La New Architecture — Fabric, TurboModules, JSI, mode Bridgeless — est désormais activée par défaut depuis React Native 0.76, et la version 0.82 a définitivement retiré l’ancien bridge JavaScript-natif. Le bénéfice est mesurable : démarrage à froid réduit de 43 %, rendu accéléré de 39 %, communication JS-natif 40× plus rapide. Ce tutoriel construit pas à pas votre première application multi-plateformes en 2026, avec Expo SDK 53, Expo Router, et un build EAS prêt pour les stores.

Prérequis

  • Un poste macOS, Linux ou Windows avec 16 Go de RAM minimum
  • Node.js 22 LTS installé
  • Bun ou pnpm (recommandé pour la vitesse) ; npm fonctionne aussi
  • Pour iOS : un Mac avec Xcode 16+ et CocoaPods
  • Pour Android : Android Studio et le JDK 17
  • Un compte Expo (gratuit) pour le build cloud EAS
  • Un téléphone physique avec l’app Expo Go pour tester
  • Niveau attendu : JavaScript ou TypeScript intermédiaire
  • Temps estimé : 90 minutes

Étape 1 — Comprendre ce qui a changé en 2026

Pendant des années, React Native a fonctionné avec un bridge qui sérialisait chaque appel entre le JavaScript et le natif en JSON, créant un goulot d’étranglement structurel. La New Architecture supprime ce bridge. JSI (JavaScript Interface) permet au moteur JS de tenir des références directes vers les objets natifs. Fabric remplace le UIManager par un système de rendu concurrent compatible avec Suspense et les transitions React 19. TurboModules charge les modules natifs à la demande, en mémoire partagée. Le mode Bridgeless rend l’ensemble cohérent et signale la fin d’une époque.

En pratique, vous n’aurez presque jamais à écrire du code natif pour profiter de ces gains : Expo SDK 53 et React Native 0.82 activent l’ensemble par défaut. Ce qui change pour vous, c’est le comportement à l’exécution — surtout les marges de manœuvre offertes par les transitions React 19 (animations qui ne bloquent plus l’UI thread) et l’accès synchrone aux mesures de layout dans la même frame que l’interaction.

Étape 2 — Installer la chaîne d’outils

L’installation se fait en deux temps : Node et le gestionnaire de paquets, puis les SDK plateformes (Android et iOS). Sur Linux ou Windows, on peut tester immédiatement sur Android sans installer Xcode. Pour livrer sur iOS, un Mac est obligatoire mais vous pouvez démarrer le projet et tester via Expo Go sur un iPhone réel sans Mac.

# Vérifier Node 22
node --version           # → v22.x.x

# Installer pnpm (rapide et économe en disque)
corepack enable && corepack prepare pnpm@latest --activate

# Installer le CLI Expo en global
pnpm add -g eas-cli

Si node --version renvoie autre chose qu’une 22.x, installer Node 22 LTS via nvm (Linux/macOS) ou le binaire officiel (Windows). Le CLI eas sert au build cloud d’Expo — c’est lui qui produira plus tard l’APK Android et l’IPA iOS sans nécessiter Xcode local. À ce stade, eas --version doit afficher au moins 14.x.

Étape 3 — Créer un projet Expo

Expo est aujourd’hui la façon recommandée d’initier un projet React Native, y compris par l’équipe core. Le template par défaut intègre Expo Router, le typage TypeScript, et la New Architecture activée d’office.

npx create-expo-app monapp --template default
cd monapp
pnpm install

L’opération prépare un projet d’environ 200 Mo (essentiellement des dépendances natives pour Android et iOS). À l’issue, la structure contient un dossier app/ qui héberge les écrans suivant la convention de routage par fichier d’Expo Router. Le fichier app/_layout.tsx est le layout racine, app/index.tsx est l’écran d’accueil. Tester immédiatement avec pnpm start, scanner le QR code dans Expo Go : l’app se charge sur le téléphone en moins de dix secondes.

Étape 4 — Construire un premier écran

L’écran de base utilise les composants View (équivalent d’un div), Text (équivalent d’un span paragraphe) et StyleSheet pour les styles. La syntaxe est volontairement proche du HTML/CSS web, mais la palette de composants est limitée et plus rigoureuse.

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

export default function Home() {
  const [count, setCount] = useState(0);
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Bonjour 👋</Text>
      <Pressable onPress={() => setCount(c => c + 1)} style={styles.btn}>
        <Text style={styles.btnText}>Cliqué {count} fois</Text>
      </Pressable>
      <Link href="/profil" style={styles.link}>Voir mon profil</Link>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
  title:     { fontSize: 32, fontWeight: "600", marginBottom: 24 },
  btn:       { backgroundColor: "#1f6feb", paddingHorizontal: 20, paddingVertical: 12, borderRadius: 8 },
  btnText:   { color: "white", fontSize: 16 },
  link:      { marginTop: 24, color: "#1f6feb" },
});

Trois points méritent l’attention. Pressable remplace l’ancien TouchableOpacity et offre un retour visuel personnalisable au tap, avec un meilleur comportement sur Android. StyleSheet.create fige les styles à l’init, ce qui permet à Fabric de les optimiser en mémoire native. Link de Expo Router est un composant déclaratif qui pointe vers app/profil.tsx — la navigation est dérivée de l’arborescence des fichiers, pas configurée à la main.

Étape 5 — Ajouter une seconde page et la navigation

Expo Router calque la navigation sur la structure de fichiers, comme Next.js côté web. Créer un nouvel écran consiste à créer un fichier dans le dossier app/, et il devient automatiquement accessible via une route correspondant à son nom.

// app/profil.tsx
import { View, Text, StyleSheet } from "react-native";
import { useLocalSearchParams } from "expo-router";

export default function Profil() {
  const { name = "Visiteur" } = useLocalSearchParams<{ name?: string }>();
  return (
    <View style={styles.c}>
      <Text style={styles.h}>Profil de {name}</Text>
      <Text>Bienvenue dans votre espace.</Text>
    </View>
  );
}
const styles = StyleSheet.create({
  c: { flex: 1, padding: 24, justifyContent: "center" },
  h: { fontSize: 24, fontWeight: "600", marginBottom: 12 },
});

Le hook useLocalSearchParams récupère les paramètres de route (par exemple /profil?name=Aida). Le typage TypeScript inline garantit que name est typé comme une string optionnelle, sans avoir à déclarer un type partagé. Recharger Expo Go : un appui sur le bouton « Voir mon profil » navigue maintenant vers le nouvel écran avec animation native par défaut.

Étape 6 — Récupérer des données depuis une API

La quasi-totalité des apps mobiles parle à un backend. fetch est natif, comme dans le navigateur, mais on l’enveloppe en pratique dans React Query ou SWR pour gérer le cache, le revalidation, l’état de chargement et les erreurs sans réinventer la roue.

pnpm add @tanstack/react-query
// app/_layout.tsx
import { Stack } from "expo-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

export default function Layout() {
  return (
    <QueryClientProvider client={queryClient}>
      <Stack screenOptions={{ headerStyle: { backgroundColor: "#1f6feb" }, headerTintColor: "white" }} />
    </QueryClientProvider>
  );
}
// app/articles.tsx
import { useQuery } from "@tanstack/react-query";
import { ActivityIndicator, FlatList, Text, View } from "react-native";

export default function Articles() {
  const { data, isPending } = useQuery({
    queryKey: ["articles"],
    queryFn: () => fetch("https://api.example.com/posts").then(r => r.json()),
  });
  if (isPending) return <ActivityIndicator size="large" />;
  return (
    <FlatList
      data={data}
      keyExtractor={(item) => String(item.id)}
      renderItem={({ item }) => (
        <View style={{ padding: 16, borderBottomWidth: 1, borderColor: "#eee" }}>
          <Text style={{ fontWeight: "600" }}>{item.title}</Text>
          <Text>{item.body}</Text>
        </View>
      )}
    />
  );
}

FlatList est l’équivalent natif d’une liste virtualisée : seuls les éléments visibles sont rendus, ce qui rend le scroll fluide même sur 10 000 entrées. Le combo React Query + FlatList est le standard de fait en 2026 pour une vue liste connectée à une API. À l’exécution, on doit voir un spinner court suivi de la liste — l’instrumentation React DevTools permet de vérifier qu’aucune cellule hors écran n’est rendue inutilement.

Étape 7 — Build EAS pour Android

Pour livrer l’app, on quitte Expo Go et on génère un binaire signé. EAS (Expo Application Services) compile en cloud, ce qui évite d’installer Xcode ou Android Studio en local.

eas login                       # première fois
eas init                        # crée le project ID
eas build -p android --profile preview

Le profil preview produit un APK directement installable sur n’importe quel Android, sans passer par le Play Store. Pour la production, on utilise --profile production qui génère un AAB (Android App Bundle) signé prêt pour la console développeur. La compilation prend 15 à 25 minutes la première fois (téléchargement des outils côté EAS), puis 5 à 8 minutes pour les builds suivants grâce au cache.

Étape 8 — Mesurer les performances

Vérifier que la New Architecture est bien active et observer les métriques temps réel se fait via Expo Dev Tools et React DevTools Profiler. Sur le terrain, ce qui compte est le cold start (temps entre le tap sur l’icône et le premier écran interactif) et le frame drop ratio (proportion de frames sautées lors d’animations).

// app/_layout.tsx
import { useEffect } from "react";
import { PerformanceObserver } from "react-native";

useEffect(() => {
  const obs = new PerformanceObserver((list) => {
    list.getEntries().forEach((e) => console.log(e.name, e.duration));
  });
  obs.observe({ entryTypes: ["measure"] });
  return () => obs.disconnect();
}, []);

L’API PerformanceObserver a été introduite en mode canary à RN 0.82 puis stabilisée en RN 0.83 (décembre 2025). Sur un Pixel 8 standard, un cold start sous 1,2 s est un objectif réaliste avec la New Architecture, contre 2,0-2,5 s typiques sur l’ancien bridge. Si vos mesures montrent une régression, vérifier que newArchEnabled: true figure bien dans app.json sous la clé expo.

Erreurs fréquentes

Symptôme Cause Solution
« Unable to resolve module » Cache Metro corrompu pnpm start --clear
App lente sur Android, fluide sur iOS Hermes désactivé Vérifier jsEngine: "hermes" dans app.json
Crash au démarrage iOS Pods non installés cd ios && pod install
Builds EAS qui timeout Plan gratuit saturé Utiliser un plan payant ou builder localement
Module natif manquant en production Plugin Expo absent du config Ajouter dans le tableau plugins de app.json
Texte non visible sur certains Android Couleur héritée non définie Toujours forcer color sur les Text

Sur un angle proche

مشاركة