UNPKG

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