Lesson 5

Customizing the Header

The native stack navigator renders a header bar at the top of each screen by default. You can customize everything about it — the title text, background color, button placement, and even replace it entirely. All customization goes through the options prop on Stack.Screen or screenOptions on Stack.Navigator.

Setting the Title

The simplest customization is changing the title text:

<Stack.Screen
  name="Details"
  component={DetailsScreen}
  options={{ title: 'Item Details' }}
/>

The name prop is used for navigation (navigate('Details')), while title controls what appears in the header. Without a title, the header shows the screen name.

Dynamic Titles from Params

You can set the title based on route params by passing a function to options:

<Stack.Screen
  name="Details"
  component={DetailsScreen}
  options={({ route }) => ({ title: route.params.title })}
/>

Now the header shows whatever title param was passed during navigation.

Styling the Header

Control colors and text styles through screenOptions on the navigator (applies to all screens) or options on individual screens:

<Stack.Navigator
  screenOptions={{
    headerStyle: { backgroundColor: '#6200ee' },
    headerTintColor: '#fff',
    headerTitleStyle: { fontWeight: 'bold' },
  }}
>
  <Stack.Screen name="Home" component={HomeScreen} />
  <Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
  • headerStyle — styles the header container. Use backgroundColor to change the bar color.
  • headerTintColor — sets the color of the title text, back button, and header icons.
  • headerTitleStyle — additional text styles for the title.

Adding Header Buttons

Use headerRight or headerLeft to place buttons in the header:

<Stack.Screen
  name="Home"
  component={HomeScreen}
  options={{
    headerRight: () => (
      <Button title="Info" onPress={() => alert('Info pressed')} />
    ),
  }}
/>

For buttons that need access to navigation, use the function form of options:

<Stack.Screen
  name="Home"
  component={HomeScreen}
  options={({ navigation }) => ({
    headerRight: () => (
      <Button
        title="Settings"
        onPress={() => navigation.navigate('Settings')}
      />
    ),
  })}
/>

Setting Options from Inside a Screen

Sometimes you need to set header options from within the screen component itself — for example, after loading data. Use navigation.setOptions:

function DetailsScreen({ navigation, route }) {
  React.useEffect(() => {
    navigation.setOptions({ title: route.params.title });
  }, [navigation, route.params.title]);

  return (
    <View style={styles.container}>
      <Text>{route.params.title}</Text>
    </View>
  );
}

Hiding the Header

To hide the header on a specific screen:

<Stack.Screen
  name="Fullscreen"
  component={FullscreenScreen}
  options={{ headerShown: false }}
/>

To hide it for all screens in a navigator, use screenOptions={{ headerShown: false }} on the navigator.

Complete Example

Here's a stack with a styled header, dynamic title, and a settings button:

import React from 'react';
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</Text>
      <Button
        title="View Profile"
        onPress={() => navigation.navigate('Profile', { name: 'Jason' })}
      />
    </View>
  );
}

function ProfileScreen({ route }) {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Hello, {route.params.name}</Text>
    </View>
  );
}

function SettingsScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Settings</Text>
    </View>
  );
}

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

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator
        screenOptions={{
          headerStyle: { backgroundColor: '#6200ee' },
          headerTintColor: '#fff',
          headerTitleStyle: { fontWeight: 'bold' },
        }}
      >
        <Stack.Screen
          name="Home"
          component={HomeScreen}
          options={({ navigation }) => ({
            title: 'My App',
            headerRight: () => (
              <Button
                title="⚙️"
                color="#fff"
                onPress={() => navigation.navigate('Settings')}
              />
            ),
          })}
        />
        <Stack.Screen
          name="Profile"
          component={ProfileScreen}
          options={({ route }) => ({ title: route.params.name })}
        />
        <Stack.Screen name="Settings" component={SettingsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

The header is purple across all screens, shows "My App" on the home screen with a settings button, and displays the user's name on the profile screen. All of this is declarative — no imperative header manipulation needed.

That covers headers. Try experimenting with headerRight, headerStyle, and setOptions in your own project — most apps need all three.