App.js <\/em>that shows a popup. I also added a few styles to have a shadow so the popup is visible, and a generous amount of margin so we can test closing the popup by clicking beside it later.<\/p>\n\n\n\nimport React, { useState } from 'react';\nimport { StyleSheet, Modal, Text, View } from 'react-native';\n\nexport default function App() {\n const [modalVisible, setModalVisible] = useState(true);\n\n return (\n <View style={styles.container}>\n <Text>Open up App.js to start working on your app!<\/text>\n <Modal visible={modalVisible}>\n <View style={styles.modal}>\n <Text>\n Popup content.\n <\/Text>\n <\/View>\n <\/Modal>\n <\/View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n backgroundColor: "white",\n alignItems: "center",\n justifyContent: "center",\n },\n modal: {\n flex: 1,\n margin: 50,\n padding: 5,\n backgroundColor: "white",\n shadowColor: "black",\n shadowOffset: {\n width: 0,\n height: 2,\n },\n shadowOpacity: 0.25,\n shadowRadius: 4,\n elevation: 5,\n },\n});\n<\/pre>\n\n\n\nThe Modal <\/em>is displayed according to its visible<\/em> prop. Right now, the value in the state is always true, so the popup will always be visible. <\/p>\n\n\n\nEven without a button or another way to change the state, the Modal <\/em>can be closed on Android with the back button (the menu button on Apple TV should have the same effect according to the documentation, but I don’t have one to test).<\/p>\n\n\n\nPressing the button calls the function specified in the onRequestClose<\/em> prop of the modal, so let’s change the state to hide the popup when it is pressed, which will show the main screen of the application : <\/p>\n\n\n\n<Modal\n visible={modalVisible}\n onRequestClose={() => setModalVisible(false)}>\n<\/pre>\n\n\n\nHow to add close button to a React Native modal<\/h2>\n\n\n\n
First, we have to add the button itself so we can then use it to close the popup. In that case, I wanted to add a small X in the top right corner of the popup, but it could be anywhere else.<\/p>\n\n\n\n
Given how the positioning work, there are two options for this:<\/p>\n\n\n\n
- Positioning absolutely the button in the top right corner. You can then do anything you want for the rest of the content, but then you run the risk of having the content overlapping the X since it’s out of the normal layout flow. If you want to use the space beside and under the button at the same time, it’s pretty much impossible, which leads us to the second option.<\/li>
- Positioning the button with flexbox, leaving space to the left of the button for a header. You can then fill in the header and the content at the bottom separately. If you’re doing something that’s meant to be a popup, having a title is also a pretty standard feature.<\/li><\/ul>\n\n\n\n
Here is what the component now looks like with an X added:<\/p>\n\n\n
\nimport React, { useState } from 'react';\nimport { StyleSheet, Modal, Text, View, TouchableOpacity } from 'react-native';\n\nexport default function App() {\n const [modalVisible, setModalVisible] = useState(true);\n\n return (\n <View style={styles.container}>\n <Text>Open up App.js to start working on your app!<\/Text>\n <Modal visible={modalVisible} onRequestClose={() => setModalVisible(false)}>\n <View style={styles.modal}>\n <View style={styles.modalHeader}>\n <View style={styles.modalHeaderContent}><Text>Other header content<\/Text><\/View>\n <TouchableOpacity><Text style={styles.modalHeaderCloseText}>X<\/Text><\/TouchableOpacity>\n <\/View>\n <View style={styles.modalContent}>\n <Text>\n Popup content.\n <\/Text>\n <\/View>\n <\/View>\n <\/Modal>\n <\/View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n backgroundColor: "white",\n alignItems: "center",\n justifyContent: "center",\n },\n modal: {\n flex: 1,\n margin: 50,\n padding: 5,\n backgroundColor: "white",\n shadowColor: "black",\n shadowOffset: {\n width: 0,\n height: 2,\n },\n shadowOpacity: 0.25,\n shadowRadius: 4,\n elevation: 5,\n },\n \/* The content of the modal takes all the vertical space not used by the header. *\/\n modalContent: {\n flex: 1\n },\n modalHeader: {\n flexDirection: "row",\n },\n \/* The header takes up all the vertical space not used by the close button. *\/\n modalHeaderContent: {\n flexGrow: 1,\n },\n modalHeaderCloseText: {\n textAlign: "center",\n paddingLeft: 5,\n paddingRight: 5\n }\n});\n<\/pre>\n\n\n\nThe header is a flex container that’s displayed as a row, with flexGrow:1<\/code> to indicates that it should take up all the remaining space.<\/p>\n\n\n\nThe rest of the content in the popup is a flex:1<\/code> item so it takes up all the remaining height. <\/p>\n\n\n\nThe only thing remaining at this point is to wire up the button so it does close the popup, in the same way we set it in on the onRequestClose <\/em>event earlier:<\/p>\n\n\n \n<TouchableOpacity onPress={() => setModalVisible(false)}>\n <Text style={styles.modalHeaderCloseText}>X<\/Text>\n<\/TouchableOpacity> \n<\/pre>\n\n\n\nHow to close the modal by clicking outside<\/h2>\n\n\n\n
To also close the modal by tapping outside, we need an additional component to catch those taps. <\/p>\n\n\n\n
On the other hand, we don’t want this component to catch taps meant for the child component: clicking the popup itself should not close it. By checking event.target\u00a0==\u00a0event.currentTarget<\/code>, we validate that the item selected is the component itself and not one of its children.<\/p>\n\n\n\nI used a Pressable<\/em> component because I didn’t want the “dimming” effect on tap when touching outside the popup that comes with the TouchableOpacity<\/em>. This new Pressable <\/em>component wraps all the components we previously defined in the modal.<\/p>\n\n\n\nHere is the completed popup, with a few extra borders to show where are the header and popup content :<\/p>\n\n\n
\nimport React, { useState } from 'react';\nimport { StyleSheet, Modal, Text, View, Pressable, TouchableOpacity } from 'react-native';\n\nexport default function App() {\n const [modalVisible, setModalVisible] = useState(true);\n return (\n <View style={styles.container}>\n <Text>Open up App.js to start working on your app!<\/Text>\n <Modal\n visible={modalVisible}\n onRequestClose={() => setModalVisible(false)}>\n <Pressable style={styles.outsideModal}\n onPress={(event) => { if (event.target == event.currentTarget) { \n setModalVisible(false); } }} >\n <View style={styles.modal}>\n <View style={styles.modalHeader}>\n <View style={styles.modalHeaderContent}><Text>Other header content<\/Text><\/View>\n <TouchableOpacity onPress={() => setModalVisible(false)}>\n <Text style={styles.modalHeaderCloseText}>X<\/Text>\n <\/TouchableOpacity>\n <\/View>\n <View style={styles.modalContent}>\n <Text>\n Popup content.\n <\/Text>\n <\/View>\n <\/View>\n <\/Pressable>\n <\/Modal>\n <\/View>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n backgroundColor: "white",\n alignItems: "center",\n justifyContent: "center",\n },\n modal: {\n flex: 1,\n margin: 50,\n padding: 5,\n backgroundColor: "white",\n shadowColor: "black",\n shadowOffset: {\n width: 0,\n height: 2,\n },\n shadowOpacity: 0.25,\n shadowRadius: 4,\n elevation: 5,\n },\n \/* The content of the modal takes all the vertical space not used by the header. *\/\n modalContent: {\n flex: 1,\n borderWidth: 1,\n borderColor: "black"\n },\n modalHeader: {\n flexDirection: "row",\n borderWidth: 1,\n borderColor: "black"\n },\n \/* The header takes up all the vertical space not used by the close button. *\/\n modalHeaderContent: {\n flexGrow: 1,\n },\n modalHeaderCloseText: {\n textAlign: "center",\n paddingLeft: 5,\n paddingRight: 5\n },\n outsideModal: {\n backgroundColor: "rgba(1, 1, 1, 0.2)",\n flex: 1,\n }\n});\n<\/pre>\n\n\n\nPlease note that there is a small limitation to this: the background color for a Pressable <\/em>or a TouchableOpacity <\/em>cannot be set to a transparent or a semi-transparent value, so the content under the popup will no longer be visible.<\/p>\n\n\n\nA next step to make this better would be to package the Modal <\/em>components and all its content into a new component that can be reused in your application so you can insert any header or content in the popup, but this is outside the scope of the current article.<\/p>\n\n\n\nIf you want to execute the final version yourself and try it out, you can see it on GitHub here: https:\/\/github.com\/CindyPotvin\/react-native-popup<\/a> and as an Expo Snack here: https:\/\/snack.expo.dev\/@cindyptn\/react-native-popup-with-x-button<\/a><\/p>\n\n\n