KeyboardAwareScrollView
ScrollView that effortlessly handles keyboard appearance, automatically scrolls to focused TextInput and provides a native-like performance.
Comparison
Current react-native ecosystem has a plenty of solutions that solves the problem of focused inputs being covered by keyboard. Each of them has its own advantages and disadvantages.
Below is a table with the most important functions and their support in various implementations:
| react-native-avoid-soft-input | react-native-keyboard-aware-scroll-view | react-native-keyboard-manager | react-native-keyboard-controller | |
|---|---|---|---|---|
| Respects keyboard animation | 🟠 1 | ❌ | ✅ | ✅ | 
| JS implementation | ❌ | ✅ | ❌ | 🟠 2 | 
| Reacts on focused input layout changes | ❌ | ❌ | 🟠 3 | ✅ | 
| Reacts on focus changes | ✅ | ✅ | ✅ | ✅ | 
| Auto-scroll when user is typing and input in non visible area | ❌ | ❌ | 🟠 3 | ✅ | 
| Android support | ✅ | ✅ | ❌ | ✅ | 
| Maintained | ✅ | ❌ | ✅ | ✅ | 
| Support Fabric (new) architecture | ✅ | 🟠 4 | ❌ | ✅ | 
1 only on iOS
2
KeyboardAwareScrollViewis implemented in JS, but some hooks (useKeyboardHandler/useReanimatedFocusedInput/useFocusedInputHandler) exposed from native code
3 achievable with
KeyboardManager.reloadLayoutIfNeeded()usage in appropriateTextInputcallbacks (onLayout/onChangeText)
4 since it's JS based solution it supports new architecture, but it uses deprecated API.
Props
ScrollView Props
Inherits ScrollView Props.
bottomOffset
The distance between keyboard and focused TextInput when keyboard is shown. Default is 0.
react-native-keyboard-aware-scroll-view equivalentThis property is equivalent to extraHeight from original react-native-keyboard-aware-scroll-view package.
disableScrollOnKeyboardHide
Prevents automatic scrolling of the ScrollView when the keyboard gets hidden, maintaining the current screen position. Default is false.
enabled
A boolean prop indicating whether KeyboardAwareScrollView is enabled or disabled. Default is true.
FlatList/FlashList/SectionList integrations
Unlike original react-native-keyboard-aware-scroll-view package I'm not exporting KeyboardAwareFlatList, KeyboardAwareSectionList and other components.
If you want to integrate it with your custom virtualization list, you can pass renderScrollComponent prop like:
import { FlatList } from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
<FlatList
  renderScrollComponent={(props) => <KeyboardAwareScrollView {...props} />}
/>;
// or
import { FlashList } from "@shopify/flash-list";
<FlashList
  renderScrollComponent={(props) => <KeyboardAwareScrollView {...props} />}
/>;
Click to see a full code example with integration
import React from "react";
import { View, FlatList, TextInput } from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
const List = () => {
  return (
    <View style={{ flex: 1 }}>
      <FlatList
        data={new Array(10).fill(0).map((_, i) => i)}
        keyExtractor={(data) => String(data)}
        renderItem={() => {
          return (
            <View
              style={{
                width: "100%",
                height: 250,
                backgroundColor: "#00000066",
                justifyContent: "center",
              }}
            >
              <TextInput
                style={{
                  height: 40,
                  width: "100%",
                  borderColor: "black",
                  borderWidth: 2,
                }}
              />
            </View>
          );
        }}
        renderScrollComponent={(props) => (
          <KeyboardAwareScrollView {...props} />
        )}
        ItemSeparatorComponent={() => <View style={{ height: 5 }} />}
        showsVerticalScrollIndicator={false}
      />
    </View>
  );
};
export default List;
Example
import React from "react";
import {
  StyleSheet,
  TextInputProps,
  TextInput as TextInputRN,
} from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
const TextInput = (props: TextInputProps) => {
  return (
    <TextInputRN
      placeholderTextColor="#6c6c6c"
      style={styles.textInput}
      multiline
      numberOfLines={2}
      testID={props.placeholder}
      {...props}
      placeholder={`${props.placeholder} (${
        props.keyboardType === "default" ? "text" : "numeric"
      })`}
    />
  );
};
export default function AwareScrollView() {
  return (
    <KeyboardAwareScrollView
      bottomOffset={50}
      style={styles.container}
      contentContainerStyle={styles.content}
    >
      {new Array(10).fill(0).map((_, i) => (
        <TextInput
          key={i}
          placeholder={`TextInput#${i}`}
          keyboardType={i % 2 === 0 ? "numeric" : "default"}
        />
      ))}
    </KeyboardAwareScrollView>
  );
}
const styles = StyleSheet.create({
  container: {
    paddingHorizontal: 16,
  },
  content: {
    paddingTop: 50,
  },
  textInput: {
    width: "100%",
    minHeight: 50,
    maxHeight: 200,
    marginBottom: 50,
    borderColor: "black",
    borderWidth: 2,
    marginRight: 160,
    borderRadius: 10,
    color: "black",
    paddingHorizontal: 12,
  },
});
Known issues
- react-native-reanimated#5567: Resizing content inside ScrollViewprevents multilineTextInputfrom growing in Fabric