First Screens: Native Components and Styles

First lesson of the Mobile Apps with React Native course, free to read: about 18 minutes, 3 exercises and 3 quiz questions. This is the catalogue's base lesson; in the student space, it is rewritten for your profile.

Objectives

  • Create a React Native project with Expo and run it on a phone or an emulator
  • Build a screen with the native components View, Text, Image, TextInput and Pressable
  • Lay out a screen with StyleSheet and Flexbox, accounting for the differences from web CSS

Native componentsStyleSheet and FlexboxExpo

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 of div.
  • Text: all text must be inside a Text. A string placed directly in a View causes an error.
  • Image: a remote image (source={{ uri }}) must be given an explicit size; a local image is loaded with source={require("./assets/logo.png")}.
  • TextInput: text input, controlled by value and onChangeText (which receives the text directly, not an event).
  • Pressable: the pressable area, with onPress. Its style can 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 is row);
  • no cascade: a style set on a View does not apply to the Text it contains. Only nested Text components inherit the style of their parent Text;
  • you combine several styles with an array: style={[styles.bouton, desactive && styles.desactive]}; false or null values 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.

  1. CodeDifficulty 2JavaScript in the browser2 hints

    Compose a button's styles

    A Pressable can receive a style function. Write styleBouton({ variante = "principal", desactive = false, presse = false }) that returns a merged style object built from STYLES (provided):

    1. start from STYLES.base;
    2. add STYLES.principal or STYLES.secondaire depending on variante;
    3. if desactive, add STYLES.desactive (and ignore presse);
    4. otherwise, if presse, add STYLES.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
    }
  2. TaskDifficulty 12 hints

    First screen on your phone

    Create a project with npx create-expo-app@latest MaBoutique, run npx expo start and open it in Expo Go (or an emulator).

    Replace the home screen (src/app/index.tsx, after npm run reset-project if you want to start from a blank project) with a screen that contains:

    1. a title Ma boutique (size 28, bold);
    2. a controlled TextInput for the first name;
    3. a “Valider” Pressable that, when pressed, displays the text Bonjour <prénom> ! below the button (local state);
    4. visual feedback when the button is pressed (opacity 0.7).

    Type Léa then 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
  3. Multiple choiceDifficulty 11 hint

    Default layout

    A View contains three Text components 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

  1. What does the function passed to onChangeText receive?

    • An event with target.value
    • The entered text
    • The length of the text
  2. What does 16 mean in fontSize: 16?

    • 16 physical pixels
    • 16 density-independent points
    • 16% of the width
  3. 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

  1. Lists and Navigation Between Screens18 min
  2. Networking, Local Storage and Offline Mode20 min
  3. Accessing the Phone: Permissions, Location, Photos, Notifications18 min
  4. Preparing and Publishing the App16 min

Mobile Apps with React Native · Intermediate · 1 h 30 min