Modal Screens
Modals slide up from the bottom of the screen and overlay the content behind them. They're used for focused tasks — composing a message, filling out a form, or confirming an action. React Navigation supports modals through the presentation screen option.
Basic Modal
Set presentation: 'modal' on a screen to present it as a modal:
<Stack.Screen
name="CreatePost"
component={CreatePostScreen}
options={{ presentation: 'modal' }}
/>
When you navigate to this screen, it slides up from the bottom instead of the standard right-to-left push. On iOS, the previous screen scales down slightly behind it — matching the native modal behavior.
Modal with a Close Button
Modals typically have a close button instead of a back arrow. Add one with headerLeft:
<Stack.Screen
name="CreatePost"
component={CreatePostScreen}
options={({ navigation }) => ({
presentation: 'modal',
title: 'New Post',
headerLeft: () => (
<Button title="Cancel" onPress={() => navigation.goBack()} />
),
})}
/>
Transparent Modals
Use presentation: 'transparentModal' for overlays that show the previous screen behind them:
<Stack.Screen
name="Alert"
component={AlertScreen}
options={{ presentation: 'transparentModal', headerShown: false }}
/>
You'll need to style the screen with a semi-transparent background:
function AlertScreen({ navigation }) {
return (
<View style={alertStyles.overlay}>
<View style={alertStyles.modal}>
<Text style={alertStyles.title}>Are you sure?</Text>
<Button title="Yes" onPress={() => navigation.goBack()} />
<Button title="Cancel" onPress={() => navigation.goBack()} />
</View>
</View>
);
}
const alertStyles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: 'rgba(0,0,0,0.5)',
justifyContent: 'center',
alignItems: 'center',
},
modal: {
backgroundColor: '#fff',
borderRadius: 12,
padding: 24,
width: '80%',
alignItems: 'center',
},
title: { fontSize: 18, fontWeight: 'bold', marginBottom: 16 },
});
A Group for Modals
A clean pattern is to use a Stack.Group to apply modal presentation to multiple screens at once:
<Stack.Navigator>
<Stack.Group>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Group>
<Stack.Group screenOptions={{ presentation: 'modal' }}>
<Stack.Screen name="CreatePost" component={CreatePostScreen} />
<Stack.Screen name="EditProfile" component={EditProfileScreen} />
</Stack.Group>
</Stack.Navigator>
All screens in the second group present as modals. This keeps the modal configuration in one place instead of repeating it on each screen.
Complete Example
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button, TextInput, StyleSheet } from 'react-native';
const Stack = createNativeStackNavigator();
function HomeScreen({ navigation }) {
return (
<View style={styles.container}>
<Text style={styles.title}>Feed</Text>
<Button title="New Post" onPress={() => navigation.navigate('CreatePost')} />
</View>
);
}
function CreatePostScreen({ navigation }) {
const [text, setText] = React.useState('');
return (
<View style={styles.modalContent}>
<TextInput
style={styles.input}
placeholder="What's on your mind?"
value={text}
onChangeText={setText}
multiline
autoFocus
/>
<Button
title="Post"
onPress={() => {
console.log('Posted:', text);
navigation.goBack();
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
title: { fontSize: 24, marginBottom: 16 },
modalContent: { flex: 1, padding: 16, paddingTop: 24 },
input: {
fontSize: 18,
minHeight: 120,
textAlignVertical: 'top',
marginBottom: 16,
},
});
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
name="CreatePost"
component={CreatePostScreen}
options={({ navigation }) => ({
presentation: 'modal',
title: 'New Post',
headerLeft: () => (
<Button title="Cancel" onPress={() => navigation.goBack()} />
),
})}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Tapping "New Post" slides the compose screen up as a modal. The cancel button and swipe-down gesture dismiss it.
In the next lesson we'll set up deep linking to navigate into specific screens from URLs.