Skip to content

iOS keyboard height can remain zero after foreground #1572

Description

@brennan-chestnut

Describe the bug

On iOS, useReanimatedKeyboardAnimation() can intermittently remain at height.value === 0 while the system keyboard is visibly open. We observed this after both normal keyboard presentation and app foregrounding.

The system keyboard state is still available at the same moment: Keyboard.metrics()?.height reports 308–335 points. This leaves views driven by the controller at their keyboard-closed position.

Sanitized production observations:

  • 4 self-healed occurrences across 2 users
  • iOS 26.2.1 and 26.5.2
  • system height: 308–335
  • controller height: 0
  • 3 checks triggered by keyboardDidShow
  • 1 check triggered when the app returned to the foreground with visible keyboard metrics

This appears related to #864, but the stale direction here is the reverse: the keyboard is visible while the controller reports closed.

Code snippet

The provider is mounted at the application root:

<KeyboardProvider preload>{children}</KeyboardProvider>

Our current application-level recovery waits two animation frames, then compares the controller and React Native system measurements:

const systemHeight = Keyboard.metrics()?.height ?? 0;
const controllerHeight = height.value;

if (systemHeight > 1 && Math.abs(controllerHeight) <= 1) {
  recoveryHeight.value = systemHeight;
  recoveryActive.value = true;
}

We run that reconciliation after keyboardDidShow, keyboardDidChangeFrame, mount, and transition to an active app state. It restores the layout successfully, but the controller's shared value itself remains stale until a later keyboard cycle.

Repo for reproducing

We do not have a deterministic reproduction repository. The issue is intermittent in production. We can provide more sanitized measurements or test a diagnostic build if that would help.

On an iOS 26.5 simulator, normal cold focus and hide/reopen cycles update the controller correctly. The simulator hides the software keyboard when the app is backgrounded, so it does not reproduce the production restoration path where the keyboard remains visible.

To Reproduce

Observed sequence:

  1. Focus a text input using a root KeyboardProvider with preload.
  2. Leave the keyboard visible.
  3. Background and later foreground the app.
  4. Intermittently, observe a visible keyboard and Keyboard.metrics()?.height > 0, while useReanimatedKeyboardAnimation().height.value === 0.

The same stale state has also occurred immediately after keyboardDidShow, so preloading or foreground restoration alone does not explain every occurrence.

Expected behavior

When the app becomes active with an already-visible keyboard, the provider should reconcile its shared height/progress values with the current keyboard frame. A current-frame query or documented reconciliation API would also let consumers restore the provider state without maintaining a parallel layout path.

Screenshots

We cannot share production screenshots, but the sanitized measurements above were captured at the same point where our fallback moved the composer above the keyboard.

Smartphone (please complete the following information):

  • Desktop OS: macOS
  • Device: production iPhones; dedicated simulator verification used an iOS 26.5 iPhone simulator
  • OS: iOS 26.2.1 and 26.5.2
  • RN version: 0.83.6
  • RN architecture: Fabric
  • JS engine: Hermes
  • Library version: 1.21.13
  • Expo version: 55.0.26
  • Reanimated version: 4.3.1

Additional context

I inspected the iOS state path in 1.22.2 before reporting this. The shared values still appear to be updated from keyboard callbacks, and I did not find a foreground/current-frame reconciliation path, so upgrading alone does not appear to address this case.

Related reports:

Metadata

Metadata

Assignees

Labels

🍎 iOSiOS specific🐛 bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions