Keyboard handling that stops covering the active input
A field-services app we ship to iOS and Android has a long job-notes form. On a Redmi 9 Power, the on-screen keyboard covers the input the user is typing into, so they type blind. On an iPhone SE, the same thing happens with the submit button at the bottom of the form. We need the active input to stay visible on both platforms without manual scroll offsets.
The tool that fixes this is `KeyboardAvoidingView`. It shifts its children up when the keyboard opens. The catch is that it behaves differently on iOS and Android, and the defaults are wrong for both.
On iOS, set `behavior="padding"`. This adjusts the bottom padding of the container, pushing content up. On Android, `behavior` does nothing useful — Android already resizes the window when the keyboard opens, so the layout adjusts on its own. The mistake is setting `behavior="padding"` on Android anyway, which double-counts the keyboard height and pushes content too far up.
The pattern we use:
import { KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
function JobNotesForm() {
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
style={{ flex: 1 }}
>
<ScrollView
contentContainerStyle={{ paddingBottom: 40 }}
keyboardShouldPersistTaps="handled"
>
{/* form inputs */}
</ScrollView>
</KeyboardAvoidingView>
);
}The `paddingBottom` on the ScrollView's content gives the last input room to clear the keyboard on Android, where the window resize alone is not always enough for inputs inside a scroll container.
`keyboardShouldPersistTaps="handled"` is the other piece. Without it, tapping outside an input to dismiss the keyboard can close it and immediately reopen it if the tap lands on another input. With `"handled"`, the keyboard stays open if the tap is on an input, and dismisses otherwise.
For a form where the submit button sits below the last input, the same setup works. The ScrollView scrolls the button into view when the keyboard pushes the layout up. No manual `scrollTo` calls, no measuring input positions with `onLayout`.
One case where `KeyboardAvoidingView` is not enough: a modal or bottom sheet with inputs. The window resize on Android does not always propagate to a modal that is positioned absolutely. For those, we track keyboard height with `Keyboard.addListener('keyboardDidShow', ...)` and apply it as a bottom offset on the sheet itself. This is more work, so we only do it when the form is actually inside a modal.
The tradeoff with `KeyboardAvoidingView` is that on iOS, the padding shift can push a header out of view if the form is tall. We accept that — the user is typing, not reading the header. If the header must stay visible, wrap only the scroll area, not the whole screen, in the `KeyboardAvoidingView`.
Test on a small Android device and an iPhone SE. The simulator does not show the real keyboard height, so use a physical device. If the input clears the keyboard by a few pixels on both, the setup is correct.