React, mais pour des applications natives
Vous connaissez React pour le web : des composants, des props, de l’état. React Native garde exactement ce modèle, mais au lieu de produire du HTML, il pilote de vrais composants natifs d’iOS et d’Android. Un <View> devient une vue native Android ou iOS, un <Text> un champ de texte natif. Il n’y a pas de DOM, pas de <div>, pas de CSS : on écrit du JavaScript (ou du TypeScript) et React Native traduit.
Le même code sert les deux plateformes, ce qui est l’intérêt principal ; quelques ajustements par plateforme restent possibles avec Platform.OS.
Démarrer avec Expo
Expo est le cadre recommandé par la documentation de React Native pour démarrer un projet. Il fournit l’outillage (création, serveur de développement, compilation) et de nombreux modules natifs prêts à l’emploi (caméra, localisation, stockage sécurisé).
npx create-expo-app@latest MaBoutique # Node.js 22.13 ou plus récent (LTS 26 conseillée)
cd MaBoutique
npx expo start
Le modèle actuel (Expo SDK 58, React Native 0.88, React 19.3) range le code dans src/ : les écrans dans src/app/, les composants dans src/components/. Il contient un écran d’exemple ; npm run reset-project le remplace par un src/app/index.tsx vierge.
npx expo start affiche un QR code. Scannez-le avec l’application Expo Go sur votre téléphone (même réseau Wi-Fi) ou appuyez sur a pour un émulateur Android, i pour le simulateur iOS (sur Mac). Chaque enregistrement recharge l’écran instantanément.
Les composants de base
import { useState } from "react";
import { View, Text, Image, TextInput, Pressable, StyleSheet } from "react-native";
export default function Accueil() {
const [nom, setNom] = useState("");
return (
<View style={styles.ecran}>
<Image source={{ uri: "https://exemple.fr/logo.png" }} style={styles.logo} />
<Text style={styles.titre}>Bienvenue</Text>
<TextInput
style={styles.champ}
value={nom}
onChangeText={setNom}
placeholder="Votre prénom"
autoCapitalize="words"
/>
<Pressable
onPress={() => console.log("Bonjour", nom)}
style={({ pressed }) => [styles.bouton, pressed && styles.boutonPresse]}
>
<Text style={styles.texteBouton}>Valider</Text>
</Pressable>
</View>
);
}
View: le conteneur, équivalent de ladiv.Text: tout texte doit être dans unText. Une chaîne placée directement dans uneViewprovoque une erreur.Image: une image distante (source={{ uri }}) doit recevoir une taille explicite ; une image locale se charge avecsource={require("./assets/logo.png")}.TextInput: la saisie, contrôlée parvalueetonChangeText(qui reçoit directement le texte, pas un événement).Pressable: la zone appuyable, aveconPress. Sonstylepeut être une fonction recevant{ pressed }pour donner un retour visuel.
D’autres composants courants : ScrollView (contenu défilant), FlatList (listes longues, leçon suivante), ActivityIndicator (chargement), Switch, Modal.
Les styles
Les styles sont des objets JavaScript, regroupés avec StyleSheet.create :
const styles = StyleSheet.create({
ecran: { flex: 1, padding: 24, justifyContent: "center", gap: 12, backgroundColor: "#fff" },
logo: { width: 96, height: 96, alignSelf: "center" },
titre: { fontSize: 28, fontWeight: "700" },
champ: { borderWidth: 1, borderColor: "#ccc", borderRadius: 8, padding: 12, fontSize: 16 },
bouton: { backgroundColor: "#0a6", padding: 14, borderRadius: 8, alignItems: "center" },
boutonPresse: { opacity: 0.7 },
texteBouton: { color: "#fff", fontSize: 16, fontWeight: "600" },
});
Les différences avec le CSS du web sont importantes :
- propriétés en camelCase (
backgroundColor), valeurs numériques sans unité : ce sont des points indépendants de la densité de l’écran ; - Flexbox partout, avec
flexDirection: "column"par défaut (sur le web, c’estrow) ; - pas de cascade : un style posé sur une
Viewne s’applique pas auTextqu’elle contient. Seuls lesTextimbriqués héritent du style de leur parentText; - on combine plusieurs styles avec un tableau :
style={[styles.bouton, desactive && styles.desactive]}; les valeursfalseounullsont ignorées et le dernier style l’emporte.
flex: 1 fait occuper à un élément tout l’espace disponible : c’est généralement le style de la vue racine d’un écran.
Zones sûres et accessibilité
Les téléphones ont des encoches et des barres système. Enveloppez vos écrans dans SafeAreaView du paquet react-native-safe-area-context (inclus dans les modèles Expo) pour éviter que le contenu passe dessous. Pensez aussi à l’accessibilité : accessibilityLabel sur un bouton icône, accessibilityRole="button" sur un Pressable, et des zones d’appui d’au moins 44 points environ.
À retenir
- Composants natifs :
View,Text(obligatoire pour le texte),Image,TextInput,Pressable. - Styles en objets, camelCase, sans unité, Flexbox en colonne par défaut, pas de cascade.
- Expo pour créer, lancer et tester sur un vrai téléphone.
Exercices
Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.
Composer les styles d'un bouton
Un
Pressablepeut recevoir une fonction de style. ÉcrivezstyleBouton({ variante = "principal", desactive = false, presse = false })qui renvoie un objet de style fusionné à partir deSTYLES(fourni) :- on part de
STYLES.base; - on ajoute
STYLES.principalouSTYLES.secondaireselonvariante; - si
desactive, on ajouteSTYLES.desactive(et on ignorepresse) ; - sinon, si
presse, on ajouteSTYLES.presse.
Les styles ajoutés plus tard écrasent les précédents, comme dans un tableau de styles React Native. Ne modifiez pas
STYLES.const STYLES = { base: { padding: 14, borderRadius: 8, opacity: 1 }, principal: { backgroundColor: "#0a6" }, secondaire: { backgroundColor: "#eee", borderWidth: 1 }, desactive: { backgroundColor: "#999", opacity: 0.6 }, presse: { opacity: 0.7 }, }; function styleBouton({ variante = "principal", desactive = false, presse = false } = {}) { // TODO }- on part de
Premier écran sur votre téléphone
Créez un projet avec
npx create-expo-app@latest MaBoutique, lanceznpx expo startet ouvrez-le dans Expo Go (ou un émulateur).Remplacez l’écran d’accueil (
src/app/index.tsx, aprèsnpm run reset-projectsi vous voulez repartir d’un projet vierge) par un écran qui contient :- un titre
Ma boutique(taille 28, gras) ; - un
TextInputcontrôlé pour le prénom ; - un
Pressable« Valider » qui, à l’appui, affiche sous le bouton le texteBonjour <prénom> !(état local) ; - un retour visuel quand le bouton est pressé (opacité 0,7).
Saisissez
Léapuis appuyez sur Valider.- L'application s'ouvre sur le téléphone ou l'émulateur et se recharge à chaque enregistrement
- Tout le texte est dans des composants Text (aucune erreur « Text strings must be rendered within a <Text> component »)
- Le bouton devient visiblement plus transparent pendant l'appui
- Après saisie de Léa et appui, le texte « Bonjour Léa ! » s'affiche
- un titre
Mise en page par défaut
Une
Viewcontient troisTextsans aucun style de mise en page. Comment sont-ils disposés ?- Côte à côte, de gauche à droite
- Les uns sous les autres, en colonne
- Superposés au même endroit
- Selon la largeur de l'écran
Quiz de fin de leçon
Que reçoit la fonction passée à onChangeText ?
- Un événement avec target.value
- Le texte saisi
- La longueur du texte
Que vaut 16 dans fontSize: 16 ?
- 16 pixels physiques
- 16 points indépendants de la densité
- 16 % de la largeur
Comment appliquer un style conditionnel en plus d’un style de base ?
- style={styles.a + styles.b}
- style={[styles.base, actif && styles.actif]}
- className="base actif"
La suite du cours
- Listes et navigation entre écrans18 min
- Réseau, stockage local et mode hors ligne20 min
- Accéder au téléphone : permissions, localisation, photos, notifications18 min
- Préparer et publier l'application16 min