A Keyboard Avoiding View for React Native in 2021
A Copy-and-Paste Solution that Supports all iOS and Android keyboards!
This post is mirrored on my blog, chrisfrew.in.
***Update October 5th, 2021 — I was contacted by a few readers of this post about creating an NPM package with this component, so that’s just what we did. Check it out! Pull requests welcome!
***Update October 28th, 2021 — Due to a breaking change in how this component is implemented with React Native 0.66, be sure to check out the new useEffect
to ensure your KeyboardShift
component is compatible with React Native version 0.66 and up!
***Update March 15th, 2022 — Still on this, would you believe it! One small thing of note: you may find you need to add a ScrollView
as a child of KeyboardShift
, because if there is no room to move the content, you can’t shift the various inputs into view!
Back in the React Native World
I’ve been deep in the React Native world recently as I develop big features for InClub, an app that enables you to join and host private events!
Our latest sprint (for “v.1.3.0🥳”) was designed to culminate in a chat functionality. That’s right — I built an entire chat function in a mobile app in 3 weeks. 😉 This wasn’t your grandma’s chat either — the full feature set included profile picture uploading, routing push notifications to the correct chat window based on sender, and creation of new channels based on various events around the app. I know, I know, most dev consultants would need teams of people to get this done…
You know how many people they needed to get the chat working?! TEAMS!
Yeah, I’m pretty awesome. 😎
All bravado aside, I had multiple cases of imposter syndrome and existential crisis during the sprint. (Did you know, for example, that Flipper screws up file uploads in development mode on React Native projects, and that commenting out a single line fixes it?) ← that little gem right there took me 2 weeks to figure out why file uploads wouldn’t work on Android devices. 😑
Alright, enough joking — let’s get into the technical info and code.
Pesky Android Keyboards
The most critical screen of an entire chat function, in my opinion, is the chat window itself. You want a clean text input and send button — luckily, we have fantastic apps to use as examples. In our case, we followed the styling and layout used in Whatsapp, Tinder, and Facebook Messenger. While iPhones of all shapes and sizes were working great with React Native’s standard KeyboardAvoidingView
component, I quickly learned that Android devices didn't like to abide by the same rules.
Alarm bells started going off when I read something on Stack Overflow (no link, I couldn’t find it again) that some phone manufacturers for Android don’t expose the keyboard API at all! So it was clear that I couldn’t reliably trust whatever info Android phones were sending to React Native’s KeyboardAvoidingView
. It was time to hunt for a different solution.
I found this potential solution from John Tucker via codeburst.io, which takes the input location and keyboard height itself and adjusts the screen accordingly — but there were two things that I didn’t like with that post. The first was that it was the old school style of class components. Second, it was utilizing deprecated methods of TextInputState.currentlyFocusedField()
and UIManager.measure()
.
Long story short, I converted the class component to a functional component with hooks and found a way to replace those two deprecated methods. TextInputState.currentlyFocusedField()
can be replaced with TextInputState.currentlyFocusedInput()
which returns not a number (like currentlyFocusedField()
), but a React ref
to the input itself. This actually makes things easier while at the same time eliminating the second deprecation of using UIManager.measure()
, since we are able to call the measure()
method directly on the ref
returned by currentlyFocusedInput()
.
In the end, we get a shiny new 2021-friendly, less than 100 line, deliciously clean keyboard shifting view component:
Simply wrap the components in your screen that need a keyboard with the <KeyboardShift>
component and enjoy the perfection:
***IMPORTANT! For React Native 0.66 and Up!
React Native has removed removeAllListeners
from Keyboard
, as shown in this pull request, so the useEffect
hook should now be:
Dependencies and Notes
Note that this solution relies on two additional libraries, @react-navigation/elements
for the header height, and @react-native-community/hooks
for the keyboard height. A repeating theme I've found in KeyboardAvoidingView
issues is the presence of React Navigation in a React Native project - so if you are in fact not using react-navigation
in your app (in all reality, not many apps go without this dependency anymore in 2021) - you could try React Native's standard KeyboardAvoidingView
solution for both iOS and Android. Otherwise, I of course suggest my solution. 😄 It works well and we are using it in production.
NPM Module
There was enough interest that we have since created an NPM package for this component:
And the GitHub repository:
Thanks and Stay Tuned 📻
I’ll likely be posting plenty more about React Native in the coming weeks months and years as we continue to build out the InClub mobile app.
Oh yeah — I should also mention, I also built a nice scaling input for the chat — you know, one that can grow to multiple lines as you write — just like the big boys. But that post will be for another day 😉.
Cheers! 🍻
-Chris