Skip to content
Learni
View all tutorials
Développement Mobile

How to Create Your First React Native App in 2026

Lire en français

Introduction

React Native is a powerful framework for building native iOS and Android apps from a single JavaScript codebase. In 2026, Expo simplifies the process for beginners—no need for Xcode or Android Studio to get started. This tutorial walks you through creating an interactive app with a counter, button, and todo list.

Why React Native? It delivers native performance, instant hot-reload, and a huge community. Imagine building an app like Instagram with JavaScript! You'll learn the basics: components, state, styles, and lists. At the end, you'll have a working app ready to deploy on Expo Go. Estimated time: 30 minutes. Perfect for web developers moving to mobile.

Prerequisites

  • Node.js 20+ installed (check with node -v)
  • An Android/iOS emulator or the Expo Go app on your smartphone
  • Basic knowledge of JavaScript and React (hooks like useState)
  • A free account on expo.dev

Install Expo CLI

terminal
npm install -g @expo/cli
expo --version

This command installs Expo CLI globally, the official tool for managing React Native projects without complex native setup. Verify with expo --version. Use npm over yarn for 2026 compatibility to avoid errors.

Create and Launch the Initial Project

Now, generate a new Expo project. Expo handles bundling, dependencies, and cross-platform development. Download Expo Go on your phone for live testing.

Initialize the Project

terminal
npx create-expo-app MyFirstApp --template blank
cd MyFirstApp
npx expo start

Creates an empty project named 'MyFirstApp' and launches it with expo start. Scan the QR code with Expo Go to see 'Hello World' on your device. The dev server runs on Metro bundler; Ctrl+C to stop.

Basic App.js with Text and Styles

App.js
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>My First React Native App!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
  },
});

This code replaces the default content with a centered container and styled title. StyleSheet.create optimizes styles like CSS-in-JS. flex:1 fills the entire screen; test hot-reload by saving.

Add Interactivity with useState

Next, add local state with useState. It's like let in JS but reactive: changes trigger re-renders. We'll create a clickable counter—perfect for understanding hooks in mobile.

Interactive Counter

App.js
import { useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';

export default function App() {
  const [count, setCount] = useState(0);

  const increment = () => setCount(count + 1);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Counter: {count}</Text>
      <TouchableOpacity style={styles.button} onPress={increment}>
        <Text style={styles.buttonText}>+1</Text>
      </TouchableOpacity>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  title: {
    fontSize: 32,
    fontWeight: 'bold',
    marginBottom: 20,
  },
  button: {
    backgroundColor: '#007AFF',
    paddingHorizontal: 20,
    paddingVertical: 10,
    borderRadius: 8,
  },
  buttonText: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold',
  },
});

Adds a count state and TouchableOpacity button that increments on onPress. Styles draw from Material Design for a native look. Pitfall: Don't forget to import useState and TouchableOpacity.

Todo List with FlatList

App.js
import { useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, TouchableOpacity, FlatList, TextInput } from 'react-native';

export default function App() {
  const [tasks, setTasks] = useState([]);
  const [input, setInput] = useState('');

  const addTask = () => {
    if (input.trim()) {
      setTasks([...tasks, { id: Date.now().toString(), text: input }]);
      setInput('');
    }
  };

  const renderItem = ({ item }) => (
    <View style={styles.taskItem}>
      <Text style={styles.taskText}>{item.text}</Text>
    </View>
  );

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Todo List</Text>
      <TextInput
        style={styles.input}
        value={input}
        onChangeText={setInput}
        placeholder="Add a task"
      />
      <TouchableOpacity style={styles.button} onPress={addTask}>
        <Text style={styles.buttonText}>Add</Text>
      </TouchableOpacity>
      <FlatList
        data={tasks}
        renderItem={renderItem}
        keyExtractor={(item) => item.id}
        style={styles.list}
      />
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    padding: 20,
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginBottom: 20,
    textAlign: 'center',
  },
  input: {
    borderWidth: 1,
    borderColor: '#ccc',
    padding: 12,
    fontSize: 16,
    borderRadius: 8,
    marginBottom: 10,
  },
  button: {
    backgroundColor: '#007AFF',
    paddingHorizontal: 20,
    paddingVertical: 12,
    borderRadius: 8,
    marginBottom: 20,
  },
  buttonText: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  list: {
    flex: 1,
  },
  taskItem: {
    backgroundColor: '#f0f0f0',
    padding: 15,
    marginVertical: 5,
    borderRadius: 8,
  },
  taskText: {
    fontSize: 16,
  },
});

Integrates FlatList for performant lists (virtualized like Android's RecyclerView). TextInput captures input; keyExtractor is required for performance. Adds tasks dynamically without full re-renders. Pitfall: Always trim() input to avoid empty tasks.

Test on a Real Device

Your app is now complete! With expo start, scan the QR code in Expo Go. For builds: eas build after eas login.

Build for Production

terminal
npx expo install expo-dev-client eas-cli
expo login
eas build --platform all

Installs EAS CLI for cloud builds. eas build generates APK/IPA files without local setup. Submit to stores with eas submit. Saves hours compared to native config.

Best Practices

  • Always use StyleSheet: 10x better performance than inline objects.
  • Hooks first: useState and useEffect for state; avoid classes.
  • FlatList for lists: Native and optimized, not map in ScrollView.
  • Test on real devices: Emulators hide real-world performance issues.
  • TypeScript from the start: npx create-expo-app --template blank-typescript.

Common Errors to Avoid

  • Forgetting flex:1 on root container: App won't fill the screen.
  • No keyExtractor on FlatList: Crashes or lags in production.
  • Unmanaged inputs: onChangeText without value makes uncontrolled fields.
  • Excessive inline styles: Slows rendering; centralize in StyleSheet.

Next Steps

Master navigation with @react-navigation. Explore animations with react-native-reanimated. For pro projects, integrate Firebase or Supabase.

Check out our Learni React Native courses: From beginner to expert in 4 weeks.