Skip to main content
Version: Next

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-inputreact-native-keyboard-aware-scroll-viewreact-native-keyboard-managerreact-native-keyboard-controller
Respects keyboard animation🟠 1
JS implementation🟠 2
Reacts on focused input layout changes🟠 3
Reacts on focus changes
Reacts on selection changes🟠 3
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 KeyboardAwareScrollView is implemented in JS, but some hooks (useKeyboardHandler/useReanimatedFocusedInput/useFocusedInputHandler) exposed from native code

3 achievable with KeyboardManager.reloadLayoutIfNeeded() usage in appropriate TextInput callbacks (onLayout/onChangeText/onSelectionChange)

4 since it's JS based solution it supports new architecture, but it uses deprecated API.

Props

ScrollView Props

Inherits ScrollView Props.

ScrollViewComponent

Custom component that will be used as a ScrollView. Default is ScrollView.

When to use it?

If you want to use ScrollView from react-native-gesture-handler you can pass it as a ScrollViewComponent prop.

import { ScrollView } from "react-native-gesture-handler";

<KeyboardAwareScrollView ScrollViewComponent={ScrollView} />;

bottomOffset

The distance between the keyboard and the caret inside a focused TextInput when the keyboard is shown. Default is 0.

react-native-keyboard-aware-scroll-view equivalent

This property is equivalent to extraHeight from original react-native-keyboard-aware-scroll-view package.

warning

If you specified snapToOffsets to your ScrollView then KeyboardAwareScrollView will automatically respect to these values and an actual bottomOffset may be bigger (but focused input will be always above the keyboard anyway).

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.

extraKeyboardSpace

Adjusting the bottom spacing of KeyboardAwareScrollView. Default is 0.

When to use it?

It can be useful when there is some space between the KeyboardAwareScrollView and the bottom edge of the screen (and you don't want the full keyboard frame to be in the bottom). In such cases, you can specify a negative value.

If you have sticky elements above the keyboard and want to extend the keyboard frame in the bottom of ScrollView, then you can specify a positive value.

react-native-keyboard-aware-scroll-view equivalent

This property acts as extraScrollHeight from original react-native-keyboard-aware-scroll-view package.

mode

Controls how keyboard space is created at the bottom of the ScrollView. Default is "insets".

ValueBehavior
"insets"Extends the scrollable area via contentInset (iOS) and ClippingScrollView (Android). Content layout is never modified — no layout reflows occur during keyboard animation. Recommended for most use cases.
"layout"Appends a spacer View as the last child of the ScrollView. The spacer participates in layout, so flex-based arrangements (e.g. justifyContent: "space-between", gap) reflow naturally when the keyboard appears.
When to use mode="layout"?

If your ScrollView content relies on flex distribution — for example a form where a submit button should stay visually pinned to the bottom of the available space — use mode="layout" so the layout engine redistributes space when the keyboard appears.

<KeyboardAwareScrollView
mode="layout"
contentContainerStyle={{ flex: 1, justifyContent: "space-between" }}
>
<FormFields />
<SubmitButton />
</KeyboardAwareScrollView>
note

mode="insets" was introduced in 1.21.0 and is the default. mode="layout" restores the pre-1.21.0 spacer-based behavior for cases where layout reflow is intentional.

Integration with 3rd party components

FlatList/FlashList/SectionList etc.

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 React from "react";
import { FlatList, ScrollView, ScrollViewProps } from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";

<FlatList
renderScrollComponent={(props) => <KeyboardAwareScrollView {...props} />}
/>;

// or

import { FlashList } from "@shopify/flash-list";

const RenderScrollComponent = React.forwardRef<ScrollView, ScrollViewProps>(
(props, ref) => <KeyboardAwareScrollView {...props} ref={ref} />,
);

<FlashList renderScrollComponent={RenderScrollComponent} />;
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;

@gorhom/bottom-sheet

To seamlessly work with @gorhom/bottom-sheet you will need to wrap KeyboardAwareScrollView in some HOCs provided by @gorhom/bottom-sheet.

BottomSheetKeyboardAwareScrollView.tsx
import { memo } from "react";
import {
KeyboardAwareScrollView,
KeyboardAwareScrollViewProps,
} from "react-native-keyboard-controller";
import {
SCROLLABLE_TYPE,
createBottomSheetScrollableComponent,
type BottomSheetScrollViewMethods,
} from "@gorhom/bottom-sheet";
import type { BottomSheetScrollViewProps } from "@gorhom/bottom-sheet/src/components/bottomSheetScrollable/types";
import Reanimated from "react-native-reanimated";

const AnimatedScrollView = Reanimated.createAnimatedComponent(
KeyboardAwareScrollView,
);
const BottomSheetScrollViewComponent = createBottomSheetScrollableComponent<
BottomSheetScrollViewMethods,
BottomSheetScrollViewProps
>(SCROLLABLE_TYPE.SCROLLVIEW, AnimatedScrollView);
const BottomSheetKeyboardAwareScrollView = memo(BottomSheetScrollViewComponent);

BottomSheetKeyboardAwareScrollView.displayName =
"BottomSheetKeyboardAwareScrollView";

export default BottomSheetKeyboardAwareScrollView as (
props: BottomSheetScrollViewProps & KeyboardAwareScrollViewProps,
) => ReturnType<typeof BottomSheetKeyboardAwareScrollView>;
index.tsx
import BottomSheet from "@gorhom/bottom-sheet";
import BottomSheetKeyboardAwareScrollView from "./BottomSheetKeyboardAwareScrollView";

export function Example() {
return (
<BottomSheet>
<BottomSheetKeyboardAwareScrollView>
{/* More content here */}
</BottomSheetKeyboardAwareScrollView>
</BottomSheet>
);
}

Methods

assureFocusedInputVisible

A method that assures that focused input is visible and not obscured by keyboard or other elements.

You may want to call it, when layout inside ScrollView changes (for example validation message appears or disappears and it shifts position of focused input).

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