react-native-keyboard-controller
repository·main·Indexed 23 days ago
https://github.com/kirillzyusko/react-native-keyboard-controllerA universal keyboard handling solution for React Native providing consistent behavior and smooth animations on iOS and Android. It includes prebuilt components like KeyboardStickyView, KeyboardAwareScrollView, and a drop-in replacement for KeyboardAvoidingView, as well as specialized tools for chat interfaces and interactive keyboard gestures. The library supports the React Native New Architecture (Fabric) and provides hooks like useKeyboardHandler and useReanimatedFocusedInput for complex animations and layout detection.
What's inside react-native-keyboard-controller
- react-native-keyboard-controller is a universal keyboard handling solution for React Native designed for smooth animations and consistent behavior across iOS and Android. It provides tools to map keyboard movement to animated values, support Reanimated, and handle interactive keyboard dismissing.
Use KeyboardAwareScrollView to handle keyboard appearance
mainKeyboardAwareScrollViewis a component that automatically scrolls to the focusedTextInputwhen the keyboard appears, providing native-like performance and respecting keyboard animations. It is designed to prevent inputs from being covered by the keyboard.Explore react-native-keyboard-controller features and components
mainThe library provides a wide range of prebuilt components and utilities for keyboard management:
- Prebuilt Components:
KeyboardStickyView,KeyboardAwareScrollView, and a reworkedKeyboardAvoidingView. - UI Enhancements:
KeyboardToolbar(with customizable previous, next, and done buttons),OverKeyboardView(to display content over the keyboard without dismissing it),KeyboardBackgroundView(to match keyboard background), andKeyboardEffects. - Specialized Views:
KeyboardChatScrollViewfor building chat interfaces andKeyboardExtenderfor adding custom buttons/UI to the keyboard. - Advanced Capabilities: Mapping keyboard movement to animated values,
keyboardWillShow/keyboardWillHideevents on Android, preloading the keyboard to avoid focus lag, and Reanimated support.
- Prebuilt Components:
Use KeyboardChatScrollView for chat layouts
mainKeyboardChatScrollViewis a specialized component designed for chat application layouts. It provides smooth 60/120 FPS animations for keyboard appearance, interactive dismissal, and content repositioning, handling behaviors that standardScrollViewcomponents struggle with.Use KeyboardGestureArea to control keyboard position via gestures
mainKeyboardGestureAreaallows you to define a specific region on the screen where user gestures will control the keyboard's position (e.g., for interactive dismissing or showing).Platform Availability:
- Android: Available on Android >= 11. For Android < 11, it renders as a
React.Fragment(no effect). - iOS: Fully supported.
<KeyboardGestureArea interpolator="ios" offset={50} textInputNativeID="composer" > <ScrollView keyboardDismissMode="interactive"> {/* The other UI components of application in your tree */} </ScrollView> <TextInput nativeID="composer" /> </KeyboardGestureArea>- Android: Available on Android >= 11. For Android < 11, it renders as a
Understand the layout-free keyboard animation approach
mainVersion 1.21 introduces a new mental model for keyboard handling to avoid 'layout thrashing'. Instead of animating
paddingBottomorheight(which triggers expensive full layout passes every frame), the library uses a scroll-based approach.By extending the scrollable area rather than shrinking the container, the library avoids re-measuring the view tree. This is achieved by mimicking iOS
contentInsetbehavior on Android, allowing for smooth 120 FPS animations without visual glitches or performance hits caused by the React Native layout engine.Build a custom keyboard animation hook for React Navigation
mainThe default
useKeyboardAnimationhook changes the AndroidsoftInputModeon component mount/unmount. In deep navigation stacks (likereact-navigation), this can cause all subsequent screens to inheritadjustResizebecause previous screens remain mounted.To ensure
softInputModeis only set toadjustResizewhile a specific screen is focused and restored to default when the screen is blurred, you can implement a custom hook usinguseFocusEffectfrom@react-navigation/nativeand the primitives fromreact-native-keyboard-controller.import { useContext, useCallback } from "react"; import { useFocusEffect } from "@react-navigation/native"; import { KeyboardController, AndroidSoftInputModes, useKeyboardContext, } from "react-native-keyboard-controller"; function useKeyboardAnimation() { useFocusEffect( useCallback(() => { KeyboardController.setInputMode( AndroidSoftInputModes.SOFT_INPUT_ADJUST_RESIZE, ); return () => KeyboardController.setDefaultMode(); }, []), ); const context = useKeyboardContext(); return context.animated; }Integrate KeyboardChatScrollView with virtualized lists (FlatList, FlashList, LegendList)
mainSince
KeyboardChatScrollViewis a custom scroll component, you can integrate it with third-party virtualized lists using theirrenderScrollComponentprop.To ensure correct behavior, create a wrapper component that forwards the ref and sets
automaticallyAdjustContentInsets={false}andcontentInsetAdjustmentBehavior="never".import React, { forwardRef } from "react"; import { KeyboardChatScrollView, type KeyboardChatScrollViewRef, } from "react-native-keyboard-controller"; import type { ScrollViewProps } from "react-native"; import type { KeyboardChatScrollViewProps } from "react-native-keyboard-controller"; const VirtualizedListScrollView = forwardRef< KeyboardChatScrollViewRef, ScrollViewProps & KeyboardChatScrollViewProps >((props, ref) => { return ( <KeyboardChatScrollView ref={ref} automaticallyAdjustContentInsets={false} contentInsetAdjustmentBehavior="never" {...props} /> ); }); export default VirtualizedListScrollView;Then use it in your list:
// For FlashList <FlashList renderScrollComponent={VirtualizedListScrollView} {...otherProps} /> // For FlatList or LegendList (memoize the component to avoid re-renders) const memoList = useCallback( (props: ScrollViewProps) => <VirtualizedListScrollView {...props} />, [], ); <FlatList renderScrollComponent={memoList} {...otherProps} />Create keyboard animations using hooks
mainTo create animations synchronized with the keyboard, use either
useKeyboardAnimationoruseReanimatedKeyboardAnimation. Both hooks return an object containingprogressandheightvalues.Choosing a hook
useKeyboardAnimation: Returns standard React NativeAnimated.Valueobjects. It has the Native Driver enabled (useNativeDriver: true), which means it is highly performant but limited to properties that support the native driver (e.g.,transform,opacity). It cannot be used to animate properties likeheightorbackgroundColor.useReanimatedKeyboardAnimation: ReturnsReanimated.SharedValueobjects. This is compatible with React Native Reanimated v2/v3 but is not compatible with the Reanimated v1 API.
Usage Pattern
- Call the hook to get
heightandprogress. - Use
progressto interpolate values (e.g., for scaling). - Apply
heightand interpolated values toAnimated.Viewstyles, typically within thetransformproperty to ensure native driver compatibility.
import React from "react"; import { Animated, StyleSheet, TextInput, View } from "react-native"; import { useKeyboardAnimation } from "react-native-keyboard-controller"; const styles = StyleSheet.create({ container: { flex: 1, alignItems: "center", justifyContent: "flex-end", }, row: { flexDirection: "row", }, }); export default function KeyboardAnimation() { // 1. get access to animated values const { height, progress } = useKeyboardAnimation(); const scale = progress.interpolate({ inputRange: [0, 1], outputRange: [1, 2], }); return ( <View style={styles.container}> <View style={styles.row}> <Animated.View style={{ width: 50, height: 50, backgroundColor: "#17fc03", borderRadius: 15, // 2. apply transformations transform: [{ translateY: height }, { scale }], }} /> </View> <TextInput style={{ width: "100%", marginTop: 50, height: 50, backgroundColor: "yellow", }} /> </View> ); }Manage multiple KeyboardEffects instances
mainMultiple mounted
KeyboardEffectsinstances share a single native keyboard state. They are reconciled using a stack mechanism similar to React Native'sStatusBar:- Each instance's
translucentvalue is added to a shared stack. - The most recently mounted (or updated) instance's value wins.
- When an instance unmounts, the keyboard falls back to the value of the next-most-recently-mounted instance still on screen, rather than defaulting immediately to opaque.
- Each instance's
Migrate from `react-native-reanimated` to `react-native-keyboard-controller`
mainStarting from
react-native-reanimated@4.2.0, theuseAnimatedKeyboardhook is deprecated. To migrate without rewriting your entire codebase, you can use the compatibility layer provided byreact-native-keyboard-controller. Simply update your import statements to pulluseAnimatedKeyboardandKeyboardStatefromreact-native-keyboard-controllerinstead ofreact-native-reanimated.-import {useAnimatedKeyboard, KeyboardState} from "react-native-reanimated"; +import {useAnimatedKeyboard, KeyboardState} from "react-native-keyboard-controller";Basic setup with KeyboardChatScrollView
mainUse
KeyboardChatScrollViewas a drop-in replacement forScrollViewto create a chat interface where the keyboard automatically pushes messages up when it appears and pulls them back when it hides. Pair it withKeyboardStickyViewto keep your input composer at the bottom of the screen.import { TextInput, View } from "react-native"; import { KeyboardChatScrollView, KeyboardStickyView, } from "react-native-keyboard-controller"; function ChatScreen() { return ( <View style={{ flex: 1 }}> <KeyboardChatScrollView> {messages.map((msg) => ( <Message key={msg.id} {...msg} /> ))} </KeyboardChatScrollView> <KeyboardStickyView> <TextInput placeholder="Type a message..." /> </KeyboardStickyView> </View> ); }