React, but for native apps
You know React for the web: components, props, state. React Native keeps exactly that model, but instead of producing HTML, it drives real native components on iOS and Android. A <View> becomes a native Android or iOS view, a <Text> a native text field. There is no DOM, no <div>, no CSS: you write JavaScript (or TypeScript) and React Native translates it.
The same code serves both platforms, which is the main benefit; a few per-platform adjustments remain possible with Platform.OS.
Getting started with Expo
Expo is the framework recommended by the React Native documentation for starting a project. It provides the tooling (creation, development server, building) and many ready-to-use native modules (camera, location, secure storage).
npx create-expo-app@latest MaBoutique # Node.js 22.13 or newer (LTS 26 recommended)
cd MaBoutique
npx expo start
The current template (Expo SDK 58, React Native 0.88, React 19.3) puts the code in src/: screens in src/app/, components in src/components/. It contains an example screen; npm run reset-project replaces it with a blank src/app/index.tsx.
npx expo start displays a QR code. Scan it with the Expo Go app on your phone (same Wi-Fi network), or press a for an Android emulator, i for the iOS simulator (on a Mac). Every save reloads the screen instantly.
The basic components
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}>Welcome</Text>
<TextInput
style={styles.champ}
value={nom}
onChangeText={setNom}
placeholder="Your first name"
autoCapitalize="words"
/>
<Pressable
onPress={() => console.log("Hello", nom)}
style={({ pressed }) => [styles.bouton, pressed && styles.boutonPresse]}
>
<Text style={styles.texteBouton}>Submit</Text>
</Pressable>
</View>
);
}
View: the container, the equivalent ofdiv.Text: all text must be inside aText. A string placed directly in aViewcauses an error.Image: a remote image (source={{ uri }}) must be given an explicit size; a local image is loaded withsource={require("./assets/logo.png")}.TextInput: text input, controlled byvalueandonChangeText(which receives the text directly, not an event).Pressable: the pressable area, withonPress. Itsstylecan be a function receiving{ pressed }to provide visual feedback.
Other common components: ScrollView (scrolling content), FlatList (long lists, next lesson), ActivityIndicator (loading), Switch, Modal.
Styles
Styles are JavaScript objects, grouped with 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" },
});
The differences from web CSS are important:
- properties are camelCase (
backgroundColor), and numeric values have no unit: they are density-independent points; - Flexbox everywhere, with
flexDirection: "column"by default (on the web, it isrow); - no cascade: a style set on a
Viewdoes not apply to theTextit contains. Only nestedTextcomponents inherit the style of their parentText; - you combine several styles with an array:
style={[styles.bouton, desactive && styles.desactive]};falseornullvalues are ignored and the last style wins.
flex: 1 makes an element take up all the available space: it is generally the style of a screen’s root view.
Safe areas and accessibility
Phones have notches and system bars. Wrap your screens in SafeAreaView from the react-native-safe-area-context package (included in the Expo templates) so that content does not slide underneath. Also think about accessibility: accessibilityLabel on an icon button, accessibilityRole="button" on a Pressable, and touch targets of at least about 44 points.
Key takeaways
- Native components:
View,Text(required for text),Image,TextInput,Pressable. - Styles as objects, camelCase, unitless, Flexbox in a column by default, no cascade.
- Expo to create, run and test on a real phone.
Exercises
Solutions and tests stay on the server. In the student space, your code is run and checked, and the tutor gives hints before the solution.
Compose a button's styles
A
Pressablecan receive a style function. WritestyleBouton({ variante = "principal", desactive = false, presse = false })that returns a merged style object built fromSTYLES(provided):- start from
STYLES.base; - add
STYLES.principalorSTYLES.secondairedepending onvariante; - if
desactive, addSTYLES.desactive(and ignorepresse); - otherwise, if
presse, addSTYLES.presse.
Styles added later override earlier ones, as in a React Native style array. Do not modify
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 }- start from
First screen on your phone
Create a project with
npx create-expo-app@latest MaBoutique, runnpx expo startand open it in Expo Go (or an emulator).Replace the home screen (
src/app/index.tsx, afternpm run reset-projectif you want to start from a blank project) with a screen that contains:- a title
Ma boutique(size 28, bold); - a controlled
TextInputfor the first name; - a “Valider”
Pressablethat, when pressed, displays the textBonjour <prénom> !below the button (local state); - visual feedback when the button is pressed (opacity 0.7).
Type
Léathen press Valider.- The app opens on the phone or emulator and reloads on every save
- All text is inside Text components (no "Text strings must be rendered within a <Text> component" error)
- The button becomes visibly more transparent while pressed
- After typing Léa and pressing, the text "Bonjour Léa !" is displayed
- a title
Default layout
A
Viewcontains threeTextcomponents with no layout style at all. How are they arranged?- Side by side, from left to right
- One below the other, in a column
- Stacked on top of each other in the same place
- Depending on the screen width
End-of-lesson quiz
What does the function passed to onChangeText receive?
- An event with target.value
- The entered text
- The length of the text
What does 16 mean in fontSize: 16?
- 16 physical pixels
- 16 density-independent points
- 16% of the width
How do you apply a conditional style in addition to a base style?
- style={styles.a + styles.b}
- style={[styles.base, actif && styles.actif]}
- className="base actif"
The rest of the course
- Lists and Navigation Between Screens18 min
- Networking, Local Storage and Offline Mode20 min
- Accessing the Phone: Permissions, Location, Photos, Notifications18 min
- Preparing and Publishing the App16 min