How to Get Keyboard Height in React-Native: Handling AutoFocus TextInput in StackNavigator with React-Navigation
In React-Native, managing the on-screen keyboard is a common challenge, especially when dealing with auto-focused text inputs. When a TextInput with autoFocus={true} loads, the keyboard pops up immediately—but if the UI isn’t adjusted, the input field (or other content) might get hidden. This is particularly tricky in a StackNavigator (from React Navigation), where screen layouts, headers, and safe areas add layers of complexity.
This guide will walk you through retrieving the keyboard height in React-Native and using it to adjust your UI dynamically, specifically for auto-focused TextInput components within a StackNavigator. By the end, you’ll ensure your text inputs are always visible when the keyboard appears, delivering a smooth user experience.
Table of Contents#
- Prerequisites
- Understanding the Problem
- Step 1: Project Setup
- Step 2: Using React-Native’s Keyboard Module
- Step 3: Handling AutoFocus TextInput
- Step 4: Integrating with StackNavigator
- Step 5: Adjusting UI with Keyboard Height
- Common Issues & Solutions
- Conclusion
- References
Prerequisites#
Before starting, ensure you have:
- Basic knowledge of React-Native and React hooks.
- Familiarity with React Navigation (we’ll use v6, the latest stable version).
- A React-Native project set up (Expo or bare React-Native).
- The following dependencies installed:
react-native(v0.60+ recommended).@react-navigation/native(v6).@react-navigation/native-stack(for StackNavigator).react-native-screensandreact-native-safe-area-context(peer dependencies for React Navigation).
Understanding the Problem#
When you set autoFocus={true} on a TextInput, the keyboard appears as soon as the screen loads. Without adjustments:
- The
TextInputmight be covered by the keyboard (common on smaller screens). StackNavigatoradds a header, reducing the available screen space—compounding the issue.
To fix this, we need to:
- Detect when the keyboard appears/hides and retrieve its height.
- Adjust the UI (e.g., shift content up) using the keyboard height.
- Account for StackNavigator’s header and safe areas to avoid misalignment.
Step 1: Project Setup#
If you’re starting from scratch, create a new Expo project and install dependencies:
1.1 Create a New Project#
expo init KeyboardHeightDemo
cd KeyboardHeightDemo 1.2 Install React Navigation#
React Navigation requires core packages and peer dependencies:
# Install React Navigation core
npm install @react-navigation/native
# Install StackNavigator
npm install @react-navigation/native-stack
# Install peer dependencies (required for React Navigation)
expo install react-native-screens react-native-safe-area-context 1.3 Configure react-native-screens#
For react-native-screens to work, add the following to App.js (at the top):
import 'react-native-screens/native-stack'; Step 2: Using React-Native’s Keyboard Module#
React-Native provides a built-in Keyboard module to listen for keyboard events. We’ll use it to detect when the keyboard shows/hides and fetch its height.
2.1 Key Keyboard Events#
keyboardDidShow: Fires when the keyboard finishes appearing.keyboardDidHide: Fires when the keyboard finishes hiding.
These events return an object with endCoordinates.height (the keyboard’s height).
2.2 Create a Custom Hook for Keyboard Height#
To reuse keyboard height logic across components, create a custom hook useKeyboardHeight.js:
// hooks/useKeyboardHeight.js
import { useState } from 'react';
import { Keyboard } from 'react-native';
import { useFocusEffect } from '@react-navigation/native';
const useKeyboardHeight = () => {
const [keyboardHeight, setKeyboardHeight] = useState(0);
// Use useFocusEffect to add/remove listeners when the screen is focused/unfocused
useFocusEffect(
() => {
// Listener for keyboard show
const onKeyboardShow = (e) => {
// e.endCoordinates.height gives the keyboard height
setKeyboardHeight(e.endCoordinates.height);
};
// Listener for keyboard hide
const onKeyboardHide = () => {
setKeyboardHeight(0);
};
// Register listeners
const showSubscription = Keyboard.addListener('keyboardDidShow', onKeyboardShow);
const hideSubscription = Keyboard.addListener('keyboardDidHide', onKeyboardHide);
// Cleanup: Remove listeners when the screen is unfocused
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}
);
return keyboardHeight;
};
export default useKeyboardHeight; Why useFocusEffect?
Unlike useEffect, useFocusEffect (from React Navigation) ensures listeners are only active when the screen is focused. This prevents memory leaks from orphaned listeners on inactive screens.
Step 3: Handling AutoFocus TextInput#
Now, create a screen with an auto-focused TextInput. We’ll use the useKeyboardHeight hook to track the keyboard.
3.1 Create the Input Screen#
Create screens/InputScreen.js:
// screens/InputScreen.js
import React from 'react';
import { View, TextInput, StyleSheet, ScrollView } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import useKeyboardHeight from '../hooks/useKeyboardHeight';
const InputScreen = () => {
// Get keyboard height from custom hook
const keyboardHeight = useKeyboardHeight();
// Get safe area insets (e.g., bottom notch/padding)
const { bottom: safeAreaBottom } = useSafeAreaInsets();
return (
<ScrollView
style={styles.container}
// Adjust padding to make space for the keyboard
contentContainerStyle={{
flexGrow: 1,
paddingBottom: keyboardHeight + safeAreaBottom, // Add safe area to keyboard height
}}
>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
placeholder="Type here..."
autoFocus={true} // Auto-focus on screen load
keyboardType="default"
returnKeyType="done"
/>
</View>
</ScrollView>
);
};
// Styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
inputContainer: {
marginTop: 40, // Add space below the StackNavigator header
paddingHorizontal: 20,
},
input: {
height: 50,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
paddingHorizontal: 15,
fontSize: 16,
},
});
export default InputScreen; Key Details:
autoFocus={true}triggers the keyboard immediately when the screen loads.ScrollViewwithcontentContainerStyle.paddingBottomensures content doesn’t get cut off by the keyboard.useSafeAreaInsets(fromreact-native-safe-area-context) adds padding for device-specific notches (e.g., iPhone bottom safe area).
Step 4: Integrating with StackNavigator#
Now, set up a StackNavigator to navigate to the InputScreen.
4.1 Create the Navigator#
Update App.js to define the StackNavigator:
// App.js
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import InputScreen from './screens/InputScreen';
// Create a StackNavigator
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
{/* Define the InputScreen as a route */}
<Stack.Screen
name="Input"
component={InputScreen}
options={{
title: 'Auto-Focus Input', // Header title
headerStyle: { backgroundColor: '#f0f0f0' },
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
} Testing the Flow:
Run the app with expo start. The InputScreen will load, the keyboard will pop up, and the TextInput will remain visible (thanks to the ScrollView padding).
Step 5: Adjusting UI with Keyboard Height#
For more control (e.g., shifting a view up instead of using ScrollView), use the keyboard height to animate or reposition elements.
Example: Shift a View Up#
Modify InputScreen.js to use a View instead of ScrollView, and adjust its bottom style:
// screens/InputScreen.js (modified)
import React from 'react';
import { View, TextInput, StyleSheet, Animated } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import useKeyboardHeight from '../hooks/useKeyboardHeight';
const InputScreen = () => {
const keyboardHeight = useKeyboardHeight();
const { bottom: safeAreaBottom } = useSafeAreaInsets();
const translateY = new Animated.Value(0); // For smooth animation
// Animate the view up when the keyboard appears
React.useEffect(() => {
Animated.timing(translateY, {
toValue: -keyboardHeight, // Shift up by keyboard height
duration: 250,
useNativeDriver: true,
}).start();
}, [keyboardHeight]);
return (
<View style={styles.container}>
{/* Animate this view */}
<Animated.View
style={[
styles.inputContainer,
{ transform: [{ translateY }] }, // Apply animation
]}
>
<TextInput
style={styles.input}
placeholder="Type here..."
autoFocus={true}
/>
</Animated.View>
</View>
);
};
// Updated styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
justifyContent: 'flex-end', // Align input to the bottom
paddingBottom: safeAreaBottom,
},
inputContainer: {
paddingHorizontal: 20,
marginBottom: 20,
},
input: {
height: 50,
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
paddingHorizontal: 15,
fontSize: 16,
},
});
export default InputScreen; Result: The TextInput (aligned to the bottom) will smoothly shift up when the keyboard appears, avoiding overlap.
Common Issues & Solutions#
1. Keyboard Height Incorrect on Android#
Issue: keyboardDidShow may return inconsistent heights on Android.
Fix: Use keyboardDidShow (most reliable cross-platform) or keyboardWillShow (iOS-only, smoother animation).
2. Memory Leaks from Unremoved Listeners#
Issue: Forgetting to remove Keyboard listeners causes leaks.
Fix: Always use useFocusEffect (instead of useEffect) to clean up listeners when the screen is unfocused.
3. Header Height Not Accounted For#
Issue: StackNavigator’s header reduces available space.
Fix: Use useHeaderHeight from @react-navigation/elements to get the header height and adjust content positioning:
import { useHeaderHeight } from '@react-navigation/elements';
const headerHeight = useHeaderHeight();
// Use headerHeight to adjust margins/padding 4. Auto-Focus Not Triggering on Android#
Issue: Some Android devices delay autoFocus until the screen is fully rendered.
Fix: Add a small delay with setTimeout:
const inputRef = useRef(null);
useEffect(() => {
const timer = setTimeout(() => {
inputRef.current?.focus();
}, 100); // 100ms delay
return () => clearTimeout(timer);
}, []);
// In TextInput: ref={inputRef} (remove autoFocus) Conclusion#
By combining React-Native’s Keyboard module, React Navigation’s StackNavigator, and dynamic UI adjustments, you can seamlessly handle auto-focused TextInput components. Key takeaways:
- Use
Keyboard.addListenerto track keyboard height. useFocusEffectensures clean listener management in navigation stacks.- Adjust UI with
ScrollViewpadding or animated transforms to avoid hidden content.
This approach works across iOS and Android, delivering a polished experience for auto-focused inputs.