React Native

Dismissing keyboard in React-Native: stop the struggle

When you create an application, you need to manage the keyboard and in theory, the principle is quite simple. When you press an input, the keyboard opens and when you press outside the input the keyboard closes. But this behavior is not trivial in React-Native and you will have to handle it by yourself.

Why is it not trivial?

I will distinguish between two types of screens here, screens wrapped with a ++code>View++/code> and a ++code>ScrollView.++/code>

If your screen is wrapped with a ++code>View++/code>, you can see below that the keyboard does not close automatically when you press outside your input.

If your screen is wrapped with a ++/code>ScrollView++/code>, we have 3 different behaviors depending on the prop ++code>keyboardShouldPersistTaps++/code> and according to ++code>ScrollView++/code> documentation :

  • never (default) : Tapping outside of the focused text input when the keyboard is up dismisses the keyboard. When this happens, children won't receive the tap.
  • always :  The keyboard will not dismiss automatically, and the Scrollview will not catch taps, but children of the scroll view can catch taps.
  • handled : The keyboard will not dismiss automatically when the tap was handled by children of the scroll view (or captured by an ancestor).

What is missing here? It is impossible to handle button press and dismiss the keyboard with a single click. With ++code>never++/code>,  need one press to dismiss the keyboard and another to handle the button. With ++code>always++/code>, we simply cannot close the keyboard and with ++code>handled++/code>, pressing the button does not dismiss the keyboard.

The common keyboard dismisser solution

I first checked on the internet to see what the current solutions were and I found this : Wrapping your application with a Pressable that dismisses the keyboard. The ++code>Pressable++/code> that dismiss the keyboard. The ++code>accessible={false}++/code> is here to avoir breaking accessibility, you can check this article if you want to learn more.

This is very simple but is equivalent to the ++code>keyboardShouldPersistTaps="handled"++/code> of the ++code>Scrollview++/code>, when we press a child button the keyboard is not dismissed and this can lead to bugs.

For example, if you want to open a ++code>BottomSheet++/code> when you click on a button by using ++code>presentation: "transparentModal"++/code> of ++code>react-navigation++/code> . You will have the following behavior on Android.

A better way to dismiss keyboard

This is how it works:

  • When you tap outside the keyboard, the component checks if the target is a text input.
  • At the end of the tap, it only dismiss the keyboard if the target is not a text input.

Here we use react-native-gesture-handler to detect the tap and dismiss the keyboard even if a child (e.g. a button) catches it. Dismissing the keyboard at the end of the tap ensures that scrolling still works with the keyboard open. We also ensure that the keyboard is not dismissed when a ++code>TextInput++/code> is pressed with a solution inspired by  ++code>Scrollview++/code>’s code using ++code>isTextInput(>. When a ++code>TextInput++/code> is mounted, it reference is added to a ++code>Set++/code> and ++code>isTextInput++/code> simply checks if the inputs ++code>Set++/code> contains the target.

⚠️ Warning

  • Your application must be wrapped with a ++code>GestureHandlerRootView++/code>
  • I recommend wrapping each screen with the ++code>KeyboardDismissPressable++/code> instead of wrapping the whole application to avoid some conflicts and to be more flexible if some pages need a different behavior.
  • There is an import not allowed by typescript for ++code>TextInputState++/code> because this state is not exposed by react-native. So this internal function may be changed in future versions of react-native.

I am open to your challenges or ideas to improve this component so feel free to contact me if you would like to discuss this topic.

Développeur mobile ?

Rejoins nos équipes