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#

  1. Prerequisites
  2. Understanding the Problem
  3. Step 1: Project Setup
  4. Step 2: Using React-Native’s Keyboard Module
  5. Step 3: Handling AutoFocus TextInput
  6. Step 4: Integrating with StackNavigator
  7. Step 5: Adjusting UI with Keyboard Height
  8. Common Issues & Solutions
  9. Conclusion
  10. 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-screens and react-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 TextInput might be covered by the keyboard (common on smaller screens).
  • StackNavigator adds a header, reducing the available screen space—compounding the issue.

To fix this, we need to:

  1. Detect when the keyboard appears/hides and retrieve its height.
  2. Adjust the UI (e.g., shift content up) using the keyboard height.
  3. 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.
  • ScrollView with contentContainerStyle.paddingBottom ensures content doesn’t get cut off by the keyboard.
  • useSafeAreaInsets (from react-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.addListener to track keyboard height.
  • useFocusEffect ensures clean listener management in navigation stacks.
  • Adjust UI with ScrollView padding or animated transforms to avoid hidden content.

This approach works across iOS and Android, delivering a polished experience for auto-focused inputs.

References#