In React Native, how do you react to the keyboard with the Keyboard module, and which of its events are available on Android versus iOS?
answer
- subscribe, then remove()
- six names, two on Android
- will-events are iOS-only
- endCoordinates.height
- dismiss also blurs
basics
~20 sKeyboard.addListener subscribes to keyboard events and returns a subscription you remove() on cleanup. iOS emits will- and did- show, hide and change-frame events; Android only keyboardDidShow and keyboardDidHide. Keyboard.dismiss() hides the keyboard and blurs the input.
solid answer
~30 s`Keyboard` is an imperative module. `Keyboard.addListener(name, cb)` returns a subscription; call `remove()` on it in a `useEffect` cleanup, since there is no per-callback `removeListener`. There are six events (`keyboardWillShow`, `keyboardDidShow`, `keyboardWillHide`, `keyboardDidHide`, `keyboardWillChangeFrame`, `keyboardDidChangeFrame`), but Android only emits `keyboardDidShow` and `keyboardDidHide`. So pick will-events on iOS for smooth timing and did-events on Android. Events carry `endCoordinates` (use `height`), plus `duration` and `easing`, which are always `0` and `'keyboard'` on Android. `Keyboard.dismiss()` hides the keyboard and removes focus, `isVisible()` and `metrics()` report the last known state, and `scheduleLayoutAnimation(event)` matches layout changes to the keyboard's motion.
code
tsx · 19 linesimport {useEffect, useState} from 'react';
import {Keyboard, Platform} from 'react-native';
export function useKeyboardHeight(): number {
const [height, setHeight] = useState(0);
useEffect(() => {
const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
const show = Keyboard.addListener(showEvent, e => setHeight(e.endCoordinates.height));
const hide = Keyboard.addListener(hideEvent, () => setHeight(0));
return () => {
show.remove();
hide.remove();
};
}, []);
return height;
}go deeper
Recall that Keyboard.addListener returns a subscription to remove in cleanup and that Keyboard.dismiss() closes the keyboard.
Know the platform split, with will-events only on iOS and just two did-events on Android, the shape of the event (endCoordinates, duration, easing) and the cached nature of isVisible and metrics.
Write listeners that are correct on both platforms, clean up reliably, and use the module for behaviour while leaving layout to KeyboardAvoidingView to avoid double-counting.
Encapsulate keyboard state in one shared hook or provider so features agree on visibility and height instead of each subscribing on its own.
## What the Keyboard module is `Keyboard` from `react-native` is an imperative module, not a component. It lets JavaScript **listen** to the software keyboard's lifecycle and **control** it. `KeyboardAvoidingView` is built on it, and you reach for it directly when the UI must respond to the keyboard in a way no component does: hiding a chat's attachment tray when typing starts, scrolling to the newest message when the keyboard opens, or tracking whether the keyboard is up. ## Events `Keyboard.addListener(eventName, callback)` subscribes to one of six events: | Event | iOS | Android | |---|---|---| | `keyboardWillShow` | yes | no | | `keyboardDidShow` | yes | yes | | `keyboardWillHide` | yes | no | | `keyboardDidHide` | yes | yes | | `keyboardWillChangeFrame` | yes | no | | `keyboardDidChangeFrame` | yes | no | **Only `keyboardDidShow` and `keyboardDidHide` exist on Android.** Code that listens only to `keyboardWillShow` works on iOS and silently never fires on Android. The docs add an older-Android caveat: on Android 10 and below, whether these events fire can depend on the activity's `android:windowSoftInputMode`. The event object carries: - **`endCoordinates`**: `{screenX, screenY, width, height}` of the keyboard where it will end up. `height` is the usual number you want. - **`duration`** and **`easing`**: on iOS, the keyboard's own animation timing. On Android `duration` is always `0` and `easing` is always `'keyboard'`. - On iOS only, **`startCoordinates`** and **`isEventFromThisApp`**. The latter says whether the event came from this app's own keyboard session. ## Subscriptions and cleanup `addListener` returns a **subscription**. Remove it with **`subscription.remove()`**, typically in a `useEffect` cleanup. There is no per-callback `removeListener` on `Keyboard`; the only bulk option is `Keyboard.removeAllListeners(eventName)`, which also removes other code's listeners and is rarely what you want. Forgetting cleanup leaves listeners attached after the screen unmounts. Their callbacks then call state setters on unmounted components and pile up each time the screen is visited. ## Control and queries - **`Keyboard.dismiss()`** hides the keyboard **and removes focus** from the active input. - **`Keyboard.isVisible()`** reports whether the keyboard is *last known* to be visible, based on the did-show and did-hide events. - **`Keyboard.metrics()`** returns the keyboard's current `endCoordinates`, or `undefined` when it is hidden. - **`Keyboard.scheduleLayoutAnimation(event)`** schedules a layout animation that matches the keyboard event's duration and easing, which helps an accessory view move in step with the keyboard on iOS. ## A cross-platform pattern 1. Pick the event per platform: will-events on iOS for smooth timing, did-events on Android because they are all there is. 2. Store what you need, such as visibility or height, in state. 3. Remove both subscriptions in the effect cleanup. 4. Read `endCoordinates.height` rather than hard-coding a keyboard size, because it varies by device, language and keyboard. ## Using it in a chat screen A chat composer typically needs three keyboard-driven behaviours, none of which is layout: 1. **Hide the attachment or emoji tray** when the keyboard opens, so the two panels do not stack. Subscribe to the show event per platform and set a state flag. 2. **Keep the newest message visible.** When the keyboard shows, ask the message list to scroll to its end. How to scroll belongs to the list, but the trigger is the keyboard event. 3. **Adjust the composer's bottom padding** for the safe-area inset, applying it only while the keyboard is hidden. All three read from the same keyboard state, so a single shared hook that subscribes once and exposes `{visible, height}` beats three components each adding their own listeners. It also keeps cleanup in one place. ## When not to use it If all you need is "keep the composer visible", `KeyboardAvoidingView` already does the measuring and layout. Hand-rolled listeners that set bottom padding tend to double-count with safe-area insets and headers. Use the module for behaviour, such as hiding, scrolling or tracking, and let a layout component handle layout.
- A React Native chat hides its attachment tray on keyboardWillShow, but on Android the tray never hides. Why?Android does not emit `keyboardWillShow`; only `keyboardDidShow` and `keyboardDidHide` exist there. The listener is attached but never called. Subscribe to `keyboardDidShow` on Android, chosen with `Platform.OS`, and accept that the change lands after the keyboard is up.
- What does Keyboard.isVisible() actually tell you?Whether the keyboard is last known to be visible. The module tracks `keyboardDidShow` and `keyboardDidHide` and remembers the latest, and `Keyboard.metrics()` returns the stored `endCoordinates` while it is shown. It is a cached answer, not a live query of the OS, so it can lag behind a keyboard that is mid-animation.
saying these in an interview costs you the question
- keyboardWillShow fires on Android as well as iOS.
- Keyboard.removeListener(name, callback) is how you unsubscribe.
- Keyboard.dismiss() hides the keyboard but keeps the input focused.
- Keyboard event duration on Android matches the keyboard's animation.
- Keyboard listeners are removed automatically when the component unmounts.