React Native Keyboard Covering TextInput on iOS and Android
The on-screen keyboard covering a focused input field is a layout problem, not a keyboard problem — React Native doesn't automatically reposition your content when the keyboard appears, and the fix requires explicitly wrapping the relevant screen content in a component designed to shift or resize the layout when the keyboard shows.
The Problem
Tapping a TextInput near the bottom of the screen brings up the on-screen keyboard, which then visually covers the input itself (or nearby content the user needs to see while typing), with no automatic scrolling or repositioning happening.
Why It Happens
React Native leaves keyboard-avoidance behavior entirely up to the developer rather than handling it automatically, and the correct approach differs between iOS and Android in ways that are easy to get only partially right:
- No
KeyboardAvoidingView(or equivalent library) is used at all, so the layout simply stays fixed in place while the keyboard renders on top of it KeyboardAvoidingViewis used, but with the wrongbehaviorprop for the platform — iOS and Android generally need different values (paddingtypically works well on iOS, while Android often needsheightor no explicit behavior at all, depending on the Android manifest'swindowSoftInputModesetting)- The component is present but not correctly sized or positioned — commonly missing a
flex: 1on itself or its parent, causing it to not actually occupy the space it needs to be able to shift content within - The Android manifest's
windowSoftInputModeis set to a value that conflicts with whatKeyboardAvoidingViewis also trying to control, causing inconsistent or doubled behavior specifically on Android
The Fix
Wrap the relevant screen content in KeyboardAvoidingView, with platform-specific behavior, since a single behavior value rarely works identically well on both platforms:
import { KeyboardAvoidingView, Platform } from 'react-native';
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
{/* screen content including TextInput */}
</KeyboardAvoidingView>
The flex: 1 on the component itself is important — without it, KeyboardAvoidingView may not actually occupy the full available space it needs to meaningfully shift or resize its content when the keyboard appears.
For screens where content might extend below the visible area once the keyboard is accounted for, combine KeyboardAvoidingView with a ScrollView, so users can scroll to reach content even when the keyboard takes up a significant portion of the screen:
<KeyboardAvoidingView style={{ flex: 1 }} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
{/* form content */}
</ScrollView>
</KeyboardAvoidingView>
On Android specifically, check your app's manifest for a conflicting windowSoftInputMode setting, since Android's own native keyboard handling can interact with KeyboardAvoidingView in ways that produce inconsistent results if both are trying to manage the same behavior:
<!-- android/app/src/main/AndroidManifest.xml -->
<activity
android:windowSoftInputMode="adjustResize"
...>
adjustResize is generally the setting that works most predictably alongside KeyboardAvoidingView's Android behavior — if this is set to adjustPan or left unset, you may see doubled shifting, insufficient shifting, or other inconsistent behavior specifically on Android even though the same code works correctly on iOS.
If you're using Expo, the relevant manifest setting is instead configured through app.json:
{
"expo": {
"android": {
"softwareKeyboardLayoutMode": "resize"
}
}
}
Still Not Working?
If KeyboardAvoidingView is correctly configured but behavior still seems inconsistent, particularly with a header or tab bar present, add a keyboardVerticalOffset to account for any fixed-height elements above the KeyboardAvoidingView that it doesn't automatically know about — this is a common gap when the component is nested below a navigation header, since KeyboardAvoidingView only knows about its own immediate positioning, not any fixed UI elements rendered by a parent navigator:
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={Platform.OS === 'ios' ? 90 : 0}
>