Edit this page
Learn how to use modals in Expo Router.
Modals are a common user interface pattern in mobile apps. They are used to present content on top of the existing screen and is used for different purposes, such as displaying confirmation alerts or standalone forms. You can create modals in your app using the following methods:
Modal
component.Each approach has its specific use case. Understanding when to use each method is important for creating a positive user experience.
The Modal
component is part of React Native's core API. Common use cases include:
Below is an example of a custom Modal
component that overlays the current screen on different platforms:
For most use cases, you can use the Modal
component and customize it according to your app's user interface requirements. For details on how to use the Modal
component and its props, see the React Native documentation.
A modal screen is a file created inside the app directory and is used as a route within the existing stack. It is used for complex interactions that need to be part of the navigation system, such as multi-step forms where you can link to a specific screen after the process completes.
Below is an example of how a modal screen works on different platforms:
To implement a modal route, create a screen called modal.tsx inside the app directory. Here's an example file structure:
app
_layout.tsx
index.tsx
modal.tsx
The above file structure produces a layout where the index
is the first route in the stack. Inside the root layout file (app/_layout.tsx), you can add the modal
route in the stack. To present it as a modal, set the presentation
option to modal
on the route.
import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" />
<Stack.Screen
name="modal"
options={{
presentation: 'modal',
}}
/>
</Stack>
);
}
You can use the Link
component to navigate to the modal screen from the index.tsx file.
import { Link } from 'expo-router';
import { StyleSheet, Text, View } from 'react-native';
export default function Home() {
return (
<View style={styles.container}>
<Text>Home screen</Text>
<Link href="/modal" style={styles.link}>
Open modal
</Link>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
link: {
paddingTop: 20,
fontSize: 20,
},
});
The modal.tsx presents the contents of the modal.
import { StyleSheet, Text, View } from 'react-native';
export default function Modal() {
return (
<View style={styles.container}>
<Text>Modal screen</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
A modal loses its previous context when it is the current screen in the navigator and is presented as a standalone screen. Its presentation and dismissal behavior are different on each platform:
router.canGoBack()
. Here's an example of how to dismiss the modal:import { Link, router} from 'expo-router';
import { StyleSheet, Text, View } from 'react-native';
export default function Modal() {
const isPresented = router.canGoBack();
return (
<View style={styles.container}>
<Text>Modal screen</Text>
{!isPresented && <Link href="../">Dismiss modal</Link>}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
By default on iOS, the modal has a dark background which hides the status bar. To change the status bar appearance, you can use the Platform
API to check if the current platform is iOS and then use the StatusBar
component to change the appearance inside the modal.tsx file.
import { StyleSheet, Text, View, Platform } from "react-native";
import { StatusBar } from "expo-status-bar";
export default function Modal() {
return (
<View style={styles.container}>
<Text>Modal screen</Text>
<StatusBar style={Platform.OS === 'ios' ? 'light' : 'auto'} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
The video above demonstrates a modal window that appears over the main content of the web page. The background dims to draw focus to the modal, which contains information for the user. This is typical behavior for web modals, where users can interact with the modal or close it to return to the main page.
You can achieve the above web modal behavior by using the transparentModal
presentation mode, styling the overlay and modal content, and utilizing react-native-reanimated
to animate the modal's presentation.
Modify your project's root layout (app/_layout.tsx) to add an options
object to the modal route:
import { Stack } from 'expo-router';
export const unstable_settings = {
initialRouteName: 'index',
};
export default function Layout() {
return (
<Stack>
<Stack.Screen name="index" />
<Stack.Screen
name="modal"
options={{
presentation: 'transparentModal',
animation: 'fade',
headerShown: false,
}}
/>
</Stack>
);
}
unstable_settings
currently works only withStack
navigators.
The above example sets the index
screen as the initialRouteName
using unstable_settings
. This ensures that the transparent modal is always rendered on top of the current screen, even when users navigate to the modal screen via a direct link.
Style the overlay and modal content in modal.tsx as shown below:
import { Link } from 'expo-router';
import { Pressable, StyleSheet, Text } from 'react-native';
import Animated, { FadeIn, SlideInDown } from 'react-native-reanimated';
export default function Modal() {
return (
<Animated.View
entering={FadeIn}
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00000040',
}}
>
{/* Dismiss modal when pressing outside */}
<Link href={'/'} asChild>
<Pressable style={StyleSheet.absoluteFill} />
</Link>
<Animated.View
entering={SlideInDown}
style={{
width: '90%',
height: '80%',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'white',
}}
>
<Text style={{ fontWeight: 'bold', marginBottom: 10 }}>Modal Screen</Text>
<Link href="/">
<Text>← Go back</Text>
</Link>
</Animated.View>
</Animated.View>
);
}
Feel free to customize the modal animations and styles to your liking.
There are different options to present a modal screen using the presentation
option on Android and iOS.
Option | Description |
---|---|
card | The new screen will be pushed onto a stack. The default animation on Android will vary depending on the OS version and theme. On iOS, it will slide from the side. |
modal | The new screen will be presented modally, allowing for a nested stack to be rendered inside the screen. |
transparentModal | The new screen will be presented modally, with the previous screen remaining visible. This allows the content below to still be seen if the screen has a translucent background. |
containedModal | On Android, fallbacks to modal . On iOS, uses UIModalPresentationCurrentContext modal style. |
containedTransparentModal | On Android, fallbacks to transparentModal . On iOS, uses UIModalPresentationOverCurrentContext modal style. |
fullScreenModal | On Android, fallbacks to modal . On iOS, uses UIModalPresentationFullScreen modal style. |
formSheet | On Android, fallbacks to modal . On iOS, uses UIModalPresentationFormSheet modal style. |