Lesson 3

Your First Stack Navigator

A stack navigator is the most fundamental navigation pattern. Screens are placed on a stack — navigate forward to push a screen on top, go back to pop it off. It matches the navigation model users expect: tap something to see a detail screen, tap back to return.

Creating a Stack Navigator

Import createNativeStackNavigator and create a navigator instance:

import { createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

createNativeStackNavigator returns an object with two components: Stack.Navigator and Stack.Screen. The navigator wraps the screens, and each screen maps a name to a component.

Defining Two Screens

Let's create a Home screen and a Details screen:

import { View, Text, Button, StyleSheet } from 'react-native';

function HomeScreen({ navigation }) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Home Screen</Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

function DetailsScreen({ navigation }) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Details Screen</Text>
      <Button
        title="Go Back"
        onPress={() => navigation.goBack()}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  title: { fontSize: 24, marginBottom: 16 },
});

Every screen component receives a navigation prop automatically. This object has the methods you'll use to move between screens:

  • navigation.navigate('ScreenName') — pushes a screen onto the stack (or jumps to it if it already exists)
  • navigation.goBack() — pops the current screen off the stack

Putting It Together

Wire the screens into the navigator inside NavigationContainer:

import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button, StyleSheet } from 'react-native';

const Stack = createNativeStackNavigator();

function HomeScreen({ navigation }) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Home Screen</Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details')}
      />
    </View>
  );
}

function DetailsScreen({ navigation }) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Details Screen</Text>
      <Button
        title="Go Back"
        onPress={() => navigation.goBack()}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
  title: { fontSize: 24, marginBottom: 16 },
});

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

initialRouteName tells the navigator which screen to show first. Without it, the first Stack.Screen in the list is used — but it's better to be explicit.

How the Stack Works

When the app loads, only HomeScreen is on the stack. Pressing "Go to Details" pushes DetailsScreen on top. The native stack navigator animates the transition — a right-to-left slide on iOS, a bottom-to-top fade on Android.

Pressing "Go Back" (or swiping from the left edge on iOS, or pressing the hardware back button on Android) pops DetailsScreen off the stack, revealing HomeScreen underneath.

You can also push the same screen multiple times. If you call navigation.push('Details') instead of navigate, it adds another copy of DetailsScreen to the stack. The difference: navigate finds an existing screen with that name and jumps to it, while push always adds a new one.

That covers the basics of stack navigation — pushing, popping, and animating between screens. Next we'll add route params so screens can pass data to each other.