react-native-keyboard-sheet
v0.1.0
Published
A keyboard-aware React Native bottom sheet with fixed header, scrollable content, fixed footer, and safe-area support.
Maintainers
Readme
react-native-keyboard-sheet
A keyboard-aware React Native bottom sheet that keeps headers and submit buttons fixed above the keyboard on Android and iOS. Only the middle content scrolls.
Screenshots
| Example app | Compact content | RTL long form with keyboard | | --- | --- | --- | | | | |
Why
Keyboard bugs usually appear when several systems compete to move the same view: KeyboardAvoidingView, modal keyboard avoidance, Android window resizing, automatic scrolling, or an iOS keyboard manager. This package owns that layout contract in one place.
Features
- Android and iOS keyboard frame handling
- Fixed header and footer
- Scrollable middle content
- Content-sized sheets with a safe maximum height
- Safe-area support
- Rotation and screen-size changes
- Optional IQKeyboardManager-style adapter
- RTL/LTR agnostic layout
- TypeScript-first API
- No native code and no required modal dependency
Installation
npm install react-native-keyboard-sheet react-native-safe-area-contextWrap the application with SafeAreaProvider if it is not already wrapped:
import { SafeAreaProvider } from "react-native-safe-area-context";
export default function App() {
return <SafeAreaProvider>{/* application */}</SafeAreaProvider>;
}Basic usage
import { useState } from "react";
import { Button, Text, TextInput } from "react-native";
import { KeyboardSheet } from "react-native-keyboard-sheet";
export function ReportSheet() {
const [visible, setVisible] = useState(false);
const [reason, setReason] = useState("");
return (
<>
<Button title="Report" onPress={() => setVisible(true)} />
<KeyboardSheet
visible={visible}
onClose={() => setVisible(false)}
header={<Text>Report content</Text>}
footer={<Button title="Submit" onPress={() => undefined} />}
>
<TextInput
multiline
onChangeText={setReason}
value={reason}
underlineColorAndroid="transparent"
/>
</KeyboardSheet>
</>
);
}The sheet measures its natural content height. When the keyboard leaves enough room, the sheet stays compact. On smaller devices, the header and footer remain fixed and only the body becomes scrollable.
iOS keyboard managers
If the app uses a global manager such as react-native-keyboard-manager, pass an adapter so the package can temporarily disable it while the sheet is visible:
import KeyboardManager from "react-native-keyboard-manager";
const keyboardManagerAdapter = {
getEnabled: () => KeyboardManager.isEnabled(),
setEnabled: (enabled: boolean) => KeyboardManager.setEnable(enabled),
};
<KeyboardSheet
keyboardManagerAdapter={keyboardManagerAdapter}
visible={visible}
onClose={close}
>
{/* content */}
</KeyboardSheet>;Memoize the adapter or define it outside the component so it retains stable identity.
getEnabled is optional, but providing it lets the sheet restore a manager that was
already disabled instead of assuming its prior state was enabled.
API
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| visible | boolean | required | Controls presentation. |
| onClose | () => void | required | Called for backdrop and system back actions. |
| children | ReactNode | required | Scrollable sheet content. |
| header | ReactNode | — | Fixed content above the scroll area. |
| footer | ReactNode | — | Fixed content above the keyboard. |
| maxHeightRatio | number | 0.88 | Maximum fraction of screen height. |
| keyboardGap | number | 8 | Gap above an open keyboard. |
| topGap | number | 12 | Minimum distance from the top safe area. |
| scrollEnabled | boolean | true | Enables body scrolling. |
| closeOnBackdropPress | boolean | true | Enables backdrop dismissal. |
| dismissKeyboardOnClose | boolean | true | Dismisses the keyboard before closing. |
| keyboardManagerAdapter | KeyboardManagerAdapter | — | Temporarily disables an external iOS keyboard manager. |
| layoutDirection | "ltr" \| "rtl" | System direction | Explicit sheet and scroll layout direction. |
| showHandle | boolean | true | Displays the drag-style visual handle. |
| backdropAccessibilityLabel | string | Dismiss | Accessible label for the dismissible backdrop. |
| onShow / onDismiss | native modal callbacks | — | Presentation lifecycle callbacks; onShow is useful for reliably focusing an input. |
The component also exposes style props for the sheet, backdrop, handle, header, footer, and scroll content.
Layout rules
Do not wrap KeyboardSheet in KeyboardAvoidingView, and do not enable another modal keyboard-avoidance mechanism around it. The component must be the only owner of keyboard movement.
On Android, use the normal adjustResize behavior (Expo:
android.softwareKeyboardLayoutMode: "resize"). The component reconciles the resized
modal viewport with the keyboard frame so any resize already applied is not applied a
second time.
On iOS, it follows docked keyboard frame changes. Floating and undocked keyboards do
not move the entire bottom-anchored sheet.
Keep the sheet mounted near the app/root screen rather than inside a parent with
clipping styles. React Native's native Modal provides the presentation boundary.
Example app
The Expo app in example includes compact, long, fixed-footer, multiline, RTL, and
external keyboard-manager scenarios:
cd example
npm install
npm run ios
# or: npm run androidDevelopment
npm install
npm run typecheck
npm test
npm run build
npm run pack:checkLicense
MIT
