Skip to content
Mobile
Skill

/react-native-keyboard-handling

Implement frame-perfect, native-feeling keyboard handling in React Native and Expo apps. Use when content is covered by the keyboard, when keyboard animations jank or "snap" on Android, when inputs are hidden behind the keyboard, when the keyboard covers fields on Android 15 /

BOOST
From plugin
skill-set
2763 skills
Install
$ npx -y skills add bidah/skill-set --skill react-native-keyboard-handling --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/react-native-keyboard-handling

Context preview

The summary Claude sees to decide when to auto-load this skill.

Implement frame-perfect, native-feeling keyboard handling in React Native and Expo apps. Use when content is covered by the keyboard, when keyboard animations jank or "snap" on Android, when inputs are hidden behind the keyboard, when the keyboard covers fields on Android 15 /

SKILL.md

react-native-keyboard-handling.SKILL.md
name: react-native-keyboard-handling
description: >-
  Implement frame-perfect, native-feeling keyboard handling in React Native and
  Expo apps. Use when content is covered by the keyboard, when keyboard
  animations jank or "snap" on Android, when inputs are hidden behind the
  keyboard, when the keyboard covers fields on Android 15 / edge-to-edge even
  though older Android worked, or when building forms, sticky footers, or chat
  input bars that must track the keyboard. Triggers: "keyboard covers input",
  "KeyboardAvoidingView not working on Android", "keyboard animation janky",
  "keyboard sticky footer", "Android 15 keyboard", "edge-to-edge keyboard",
  react-native-keyboard-controller.

React Native Keyboard Handling

Make the keyboard feel native on both platforms by mapping two fundamentally different OS keyboard models onto **one animated value** that your UI binds to.

The core mental model (read this first)

iOS and Android expose the keyboard in incompatible ways. Most "broken keyboard" bugs come from ignoring this asymmetry.

  • **iOS = scheduled animation.** The OS tells you up front: "the keyboard will

slide from A to B over 0.25s with this easing curve." You get two snapshots (start/end) plus duration and curve — *not* intermediate frames. UIKit interpolates for you.

  • **Android (modern, edge-to-edge) = per-frame insets.** The IME is just another

system window. You get a firehose of frames, one inset value per frame, and you drive the animation yourself.

> Anything that ignores this asymmetry will feel broken. The fix is to map both > models onto a single shared animated value, then have every consumer (padded > container, translated footer, scroll offset) bind to that one value and stop > caring which platform feeds it.

Decision guide — which tool to reach for

| Situation | Use | |---|---| | Whole screen / single input should stay above keyboard | `KeyboardAvoidingView` (from `react-native-keyboard-controller`) | | Scrollable form with multiple inputs | `KeyboardAwareScrollView` | | One element pinned above the keyboard (footer, send bar, toolbar) | `KeyboardStickyView` | | Fully custom animation tied to keyboard position | `useReanimatedKeyboardAnimation()` / `useKeyboardHandler()` + Reanimated `SharedValue` |

**Default recommendation:** use [`react-native-keyboard-controller`](https://github.com/kirillzyusko/react-native-keyboard-controller), not React Native's built-in `KeyboardAvoidingView`. The built-in version relies on `LayoutAnimation` and the late `keyboardDidShow` event, which has no per-frame Android primitive — so Android content snaps instead of sliding and the timing never matches the keyboard curve.

Setup

1. Install the library and its peer deps:

   npm install react-native-keyboard-controller react-native-reanimated

2. Wrap your app root **once** in `KeyboardProvider`. This installs the native `WindowInsetsAnimationCallback` subscription on Android and handles edge-to-edge automatically:

   import { KeyboardProvider } from "react-native-keyboard-controller";

   export default function App() {
     return (
       <KeyboardProvider>
         {/* ...rest of the app */}
       </KeyboardProvider>
     );
   }

Patterns

Single input / simple screen — drop-in replacement

Same API as RN's `KeyboardAvoidingView`; swapping the import alone fixes the Android timing/snap problems.

import { KeyboardAvoidingView } from "react-native-keyboard-controller";

<KeyboardAvoidingView behavior="padding" style={{ flex: 1 }}>
  {/* input(s) */}
</KeyboardAvoidingView>

Scrollable form — track the focused input

Auto-scrolls the minimum distance to keep the focused input (and caret/selection) above the keyboard, using the same curve as the keyboard motion. `bottomOffset` adds breathing room between the input and the keyboard top.

import { KeyboardAwareScrollView } from "react-native-keyboard-controller";

<KeyboardAwareScrollView bottomOffset={50} style={{ flex: 1 }}>
  <TextInput placeholder="Field 1" />
  <TextInput placeholder="Field 2" />
  {/* more inputs */}
</KeyboardAwareScrollView>

Sticky footer / send bar — translate one element only

Translates a single element instead of recomputing flex layout for the whole screen. `offset` controls the gap when the keyboard is open vs closed.

import { KeyboardStickyView } from "react-native-keyboard-controller";

<KeyboardStickyView offset={{ closed: 0, opened: 20 }}>
  <Footer />
</KeyboardStickyView>

Custom animation — bind to the shared keyboard value

For fully bespoke motion, read the keyboard height as a Reanimated `SharedValue` and drive any style from it. Works identically on both platforms because the library already merged the two OS models into this one value.

import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
import Animated, { useAnimatedStyle } from "react-native-reanimated";

const { height } = useReanimatedKeyboardAnimation(); // SharedValue, per-frame
const style = useAnimatedStyle(() => ({
  transform: [{ translateY: height.value }],
}));

<Animated.View style={style}>{/* ... */}</Animated.View>;

Pitfalls & gotchas

  • **Android 15 / SDK 35 covers your inputs.** Edge-to-edge is forced by default

from Android 15 (API 35). The system no longer resizes the window when the keyboard appears, so the old `adjustResize` / `windowSoftInputMode` contract silently breaks — inputs get covered even though the exact same code worked on Android 14. `KeyboardProvider` handles edge-to-edge for you; don't rely on `adjustResize`.

  • **`will*` events never fire on Android.** `keyboardWillShow` / `keyboardWillHide`

have no underlying Android primitive, and `keyboardDidShow` fires *after* the animation finishes — too late to animate in sync. Don't build animations on the raw `Keyboard` event listeners for Android.

  • **Stock `KeyboardAvoidingView`
Read more
Ships withskill-set

A collection of Claude Code skills.

Get the whole plugin

Other skills on skill-set.