UNPKG

18.8 kBMarkdownView Raw
1# react-native-modal
2
3[![npm version](https://badge.fury.io/js/react-native-modal.svg)](https://badge.fury.io/js/react-native-modal)
4[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
5
6An enhanced, animated and customizable react-native modal.
7
8The goal of `react-native-modal` is expanding the original react-native `Modal` component by adding animations and styles customization options while still providing a plain-simple API.
9
10<p align="center">
11<img src="/.github/images/example-modal.gif" height="500" />
12</p>
13
14## Features
15
16- Smooth enter/exit animations
17- Plain simple and flexible APIs
18- Customizable backdrop opacity, color and timing
19- Listeners for the modal animations ending
20- Resize itself correctly on device rotation
21- Swipeable
22- Scrollable
23
24## Setup
25
26This library is available on npm, install it with: `npm install --save react-native-modal` or `yarn add react-native-modal`.
27
28## Usage
29
30Since react-native-modal is an extension of the original react native modal, it works in a similar fashion [react-native original modal](https://facebook.github.io/react-native/docs/modal.html).
31
321. Import react-native-modal:
33
34```javascript
35import Modal from "react-native-modal";
36```
37
382. Create a modal and nest its content inside of it:
39
40```javascript
41render () {
42 return (
43 <View>
44 <Modal>
45 <View style={{ flex: 1 }}>
46 <Text>I am the modal content!</Text>
47 </View>
48 </Modal>
49 </View>
50 )
51 }
52```
53
543. Then simply show it by setting the `isVisible` prop to true:
55
56```javascript
57render () {
58 return (
59 <View>
60 <Modal isVisible={true}>
61 <View style={{ flex: 1 }}>
62 <Text>I am the modal content!</Text>
63 </View>
64 </Modal>
65 </View>
66 )
67 }
68```
69
70The `isVisible` prop is the only prop you'll really need to make the modal work: you should control this prop value by saving it in your state and setting it to `true` or `false` when needed.
71
72## A complete example
73
74The following example consists in a component (`ModalTester`) with a button and a modal.
75The modal is controlled by the `isModalVisible` state variable and it is initially hidden, since its value is `false`.
76Pressing the button sets `isModalVisible` to true, making the modal visible.
77Inside the modal there is another button that, when pressed, sets `isModalVisible` to false, hiding the modal.
78
79```javascript
80import React, { Component } from "react";
81import { Button, Text, View } from "react-native";
82import Modal from "react-native-modal";
83
84export default class ModalTester extends Component {
85 state = {
86 isModalVisible: false
87 };
88
89 toggleModal = () => {
90 this.setState({ isModalVisible: !this.state.isModalVisible });
91 };
92
93 render() {
94 return (
95 <View style={{ flex: 1 }}>
96 <Button title="Show modal" onPress={this.toggleModal} />
97 <Modal isVisible={this.state.isModalVisible}>
98 <View style={{ flex: 1 }}>
99 <Text>Hello!</Text>
100 <Button title="Hide modal" onPress={this.toggleModal} />
101 </View>
102 </Modal>
103 </View>
104 );
105 }
106}
107```
108
109For a more complex example take a look at the `/example` directory.
110
111## Available props
112
113| Name | Type | Default | Description |
114| ------------------------------ | ---------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
115| animationIn | string or object | 'slideInUp' | Modal show animation |
116| animationInTiming | number | 300 | Timing for the modal show animation (in ms) |
117| animationOut | string or object | 'slideOutDown' | Modal hide animation |
118| animationOutTiming | number | 300 | Timing for the modal hide animation (in ms) |
119| avoidKeyboard | bool | false | Move the modal up if the keyboard is open |
120| coverScreen | bool | true | Will use RN `Modal` component to cover the entire screen wherever the modal is mounted in the component hierarchy |
121| hasBackdrop | bool | true | Render the backdrop |
122| backdropColor | string | 'black' | The backdrop background color |
123| backdropOpacity | number | 0.70 | The backdrop opacity when the modal is visible |
124| backdropTransitionInTiming | number | 300 | The backdrop show timing (in ms) |
125| backdropTransitionOutTiming | number | 300 | The backdrop hide timing (in ms) |
126| customBackdrop | node | null | The custom backdrop element |
127| children | node | **REQUIRED** | The modal content |
128| deviceHeight | number | null | Device height (useful on devices that can hide the navigation bar) |
129| deviceWidth | number | null | Device width (useful on devices that can hide the navigation bar) |
130| isVisible | bool | **REQUIRED** | Show the modal? |
131| onBackButtonPress | func | () => null | Called when the Android back button is pressed |
132| onBackdropPress | func | () => null | Called when the backdrop is pressed |
133| onModalWillHide | func | () => null | Called before the modal hide animation begins |
134| onModalHide | func | () => null | Called when the modal is completely hidden |
135| onModalWillShow | func | () => null | Called before the modal show animation begins |
136| onModalShow | func | () => null | Called when the modal is completely visible |
137| onSwipeStart | func | () => null | Called when the swipe action started |
138| onSwipeMove | func | (percentageShown) => null | Called on each swipe event |
139| onSwipeComplete | func | ({ swipingDirection }) => null | Called when the `swipeThreshold` has been reached |
140| onSwipeCancel | func | () => null | Called when the `swipeThreshold` has not been reached |
141| scrollOffset | number | 0 | When > 0, disables swipe-to-close, in order to implement scrollable content |
142| scrollOffsetMax | number | 0 | Used to implement overscroll feel when content is scrollable. See `/example` directory |
143| scrollTo | func | null | Used to implement scrollable modal. See `/example` directory for reference on how to use it |
144| scrollHorizontal | bool | false | Set to true if your scrollView is horizontal (for a correct scroll handling) |
145| swipeThreshold | number | 100 | Swiping threshold that when reached calls `onSwipeComplete` |
146| swipeDirection | string or array | null | Defines the direction where the modal can be swiped. Can be 'up', 'down', 'left, or 'right', or a combination of them like `['up','down']` |
147| useNativeDriver | bool | false | Defines if animations should use native driver |
148| hideModalContentWhileAnimating | bool | false | Enhances the performance by hiding the modal content until the animations complete |
149| propagateSwipe | bool | false | Allows swipe events to propagate to children components (eg a ScrollView inside a modal) |
150| style | any | null | Style applied to the modal |
151
152## Frequently Asked Questions
153
154### The component is not working as expected
155
156Under the hood `react-native-modal` uses react-native original [Modal component](https://facebook.github.io/react-native/docs/modal.html).
157Before reporting a bug, try swapping `react-native-modal` with react-native original Modal component and, if the issue persists, check if it has already been reported as a [react-native issue](https://github.com/facebook/react-native/issues).
158
159### The backdrop is not completely filled/covered on some Android devices (Galaxy, for one)
160
161React-Native has a few issues detecting the correct device width/height of some devices.
162If you're experiencing this issue, you'll need to install [`react-native-extra-dimensions-android`](https://github.com/Sunhat/react-native-extra-dimensions-android).
163Then, provide the real window height (obtained from `react-native-extra-dimensions-android`) to the modal:
164
165```javascript
166render() {
167 const deviceWidth = Dimensions.get("window").width;
168 const deviceHeight = Platform.OS === "ios"
169 ? Dimensions.get("window").height
170 : require("react-native-extra-dimensions-android").get("REAL_WINDOW_HEIGHT");
171
172 return (
173 <Modal
174 isVisible={this.state.isVisible}
175 deviceWidth={deviceWidth}
176 deviceHeight={deviceHeight}
177 >
178 <View style={{ flex: 1 }}>
179 <Text>I am the modal content!</Text>
180 </View>
181 </Modal>
182 )
183}
184```
185
186### How can I hide the modal by pressing outside of its content?
187
188The prop `onBackdropPress` allows you to handle this situation:
189
190```javascript
191<Modal
192 isVisible={this.state.isVisible}
193 onBackdropPress={() => this.setState({ isVisible: false })}
194>
195 <View style={{ flex: 1 }}>
196 <Text>I am the modal content!</Text>
197 </View>
198</Modal>
199```
200
201### How can I hide the modal by swiping it?
202
203The prop `onSwipeComplete` allows you to handle this situation (remember to set `swipeDirection` too!):
204
205```javascript
206<Modal
207 isVisible={this.state.isVisible}
208 onSwipeComplete={() => this.setState({ isVisible: false })}
209 swipeDirection="left"
210>
211 <View style={{ flex: 1 }}>
212 <Text>I am the modal content!</Text>
213 </View>
214</Modal>
215```
216
217Note that when using `useNativeDriver={true}` the modal won't drag correctly. This is a [known issue](https://github.com/react-native-community/react-native-modal/issues/163#issuecomment-409760695).
218
219### The modal flashes in a weird way when animating
220
221Unfortunately this is a [know issue](https://github.com/react-native-community/react-native-modal/issues/92) that happens when `useNativeDriver=true` and must still be solved.
222In the meanwhile as a workaround you can set the `hideModalContentWhileAnimating` prop to `true`: this seems to solve the issue.
223Also, do not assign a `backgroundColor` property directly to the Modal. Prefer to set it on the child container.
224
225### The modal background doesn't animate properly
226
227Are you sure you named the `isVisible` prop correctly? Make sure it is spelled correctly: `isVisible`, not `visible`.
228
229### The modal doesn't change orientation
230
231Add a `supportedOrientations={['portrait', 'landscape']}` prop to the component, as described [in the React Native documentation](https://facebook.github.io/react-native/docs/modal.html#supportedorientations).
232
233Also, if you're providing the `deviceHeight` and `deviceWidth` props you'll have to manually update them when the layout changes.
234
235### I can't show multiple modals one after another
236
237Unfortunately right now react-native doesn't allow multiple modals to be displayed at the same time.
238This means that, in `react-native-modal`, if you want to immediately show a new modal after closing one you must first make sure that the modal that your closing has completed its hiding animation by using the `onModalHide` prop.
239
240### I can't show multiple modals at the same time
241
242See the question above.
243Showing multiple modals (or even alerts/dialogs) at the same time is not doable because of a react-native bug.
244That said, I would strongly advice against using multiple modals at the same time because, most often than not, this leads to a bad UX, especially on mobile (just my opinion).
245
246### The StatusBar style changes when the modal shows up
247
248This issue has ben discussed [here](https://github.com/react-native-community/react-native-modal/issues/50).
249The TLDR is: it's a know React-Native issue with the Modal component 😞
250
251### The modal is not covering the entire screen
252
253The modal style applied by default has a small margin.
254If you want the modal to cover the entire screen you can easily override it this way:
255
256```js
257<Modal style={{ margin: 0 }}>...</Modal>
258```
259
260### I can't scroll my ScrollView inside of the modal
261
262Enable propagateSwipe to allow your child components to receive swipe events:
263
264```js
265<Modal propagateSwipe>...</Modal>
266```
267
268Please notice that this is still a WIP fix and might not fix your issue yet, see [issue #236](https://github.com/react-native-community/react-native-modal/issues/236).
269
270### The modal enter/exit animation flickers
271
272Make sure your `animationIn` and `animationOut` are set correctly.
273We noticed that, for example, using `fadeIn` as an exit animation makes the modal flicker (it should be `fadeOut`!).
274Also, some users have noticed that setting backdropTransitionOutTiming={0} can fix the flicker without affecting the animation.
275
276### The custom backdrop doesn't fill the entire screen
277
278You need to specify the size of your custom backdrop component. You can also make it expand to fill the entire screen by adding a `flex: 1` to its style:
279
280```javascript
281<Modal
282 isVisible={this.state.isVisible}
283 customBackdrop={<View style={{ flex: 1 }} />}
284>
285 <View style={{ flex: 1 }}>
286 <Text>I am the modal content!</Text>
287 </View>
288</Modal>
289```
290
291### The custom backdrop doesn't dismiss the modal on press
292
293You can provide an event handler to the custom backdrop element to dismiss the modal. The prop `onBackdropPress` is not supported for a custom backdrop.
294
295```javascript
296<Modal
297 isVisible={this.state.isVisible}
298 customBackdrop={
299 <TouchableWithoutFeedback onPress={dismissModalHandler}>
300 <View style={{ flex: 1 }} />
301 </TouchableWithoutFeedback>
302 }
303/>
304
305```
306
307## Available animations
308
309Take a look at [react-native-animatable](https://github.com/oblador/react-native-animatable) to see the dozens of animations available out-of-the-box. You can also pass in custom animation definitions and have them automatically register with react-native-animatable. For more information on creating custom animations, see the react-native-animatable [animation definition schema](https://github.com/oblador/react-native-animatable#animation-definition-schema).
310
311## Acknowledgements
312
313Thanks [@oblador](https://github.com/oblador) for react-native-animatable, [@brentvatne](https://github.com/brentvatne) for the npm namespace and to anyone who contributed to this library!
314
315Pull requests, feedbacks and suggestions are welcome!