useAnimatedKeyboard causes navigation bar jump with React Navigation on Android
Description
My navigation bar unexpectedly jumps or shifts when transitioning to a screen implementing useAnimatedKeyboard on Android. This issue disappears when the hook is removed.
Video demo
With useAnimatedKeyboard
https://github.com/user-attachments/assets/b5845fbe-60db-4fa5-a9e0-1fc2f53276d8
Without useAnimatedKeyboard
https://github.com/user-attachments/assets/c1480f5c-3ea7-4503-84ef-268273544686
The only code change between those 2 videos was removing this line in the screen I transitioned to.
const keyboard = useAnimatedKeyboard();
I was able to reproduce the issue on a real device as well as on an Android emulator.
Steps to reproduce
1. Set up a project with React Navigation and Reanimated.
2. Add useAnimatedKeyboard to a component
3. Run the app and navigate to the screen implementing the hook.
Snack or a link to a repository
https://snack.expo.dev/@alexmngn/supportive-yellow-watermelon
Reanimated version
3.16.7
React Native version
0.75.4
React Navigation version
7.0.14
Platforms
Android
JavaScript runtime
Hermes
Workflow
React Native
Architecture
Paper (Old Architecture)
Build type
Debug app & dev bundle
Device
Android emulator Real device
Device model
Galaxy A22
Acknowledgements
Yes
I'm running into this same issue, though I'm not using headers in my pages.
It's also causing a weird interaction with a basic SafeAreaWrapper component I have, where it adds a small, blank, "header" at the top of my screen maybe 25 pixels tall. I'm assuming it's the same height as the status bar, as that's part of my SafeAreaWrapper component, but this blank bar only appears in components that use useAnimatedKeyboard. It's the color that I define as the background color in my theme object on the NavigationContainer component from React Navigation.
I've looked into the code for useAnimatedKeyboard a bit but haven't had any luck determining what the issue might be
I too have issues with useAnimatedKeyboard on Android. This patch solved it for me
diff --git a/android/src/main/java/com/swmansion/reanimated/keyboard/WindowsInsetsManager.java b/android/src/main/java/com/swmansion/reanimated/keyboard/WindowsInsetsManager.java
index 5660944825836f4f79d82876773c58ca3a4a1368..a0cbe7be5472b872a4afcb256dcc0dcdc2314f55 100644
--- a/android/src/main/java/com/swmansion/reanimated/keyboard/WindowsInsetsManager.java
+++ b/android/src/main/java/com/swmansion/reanimated/keyboard/WindowsInsetsManager.java
@@ -56,7 +56,6 @@ public class WindowsInsetsManager {
public void stopObservingChanges() {
updateWindowDecor(!mIsStatusBarTranslucent && !mIsNavigationBarTranslucent);
- updateInsets(0, 0);
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
I face the same issue and solve it by using useAnimatedKeyboard() hook at the global or upper level in my app
I face the same issue +
I face the same issue and solve it by using useAnimatedKeyboard() hook at the global or upper level in my app
goat
same issue +1
same issue +
same
@alexmngn hey! Despite not having translucency, adding the following properties to useAnimatedKeyboard solved the issue. I believe it is related to expo router's internal stuff. Could you try it?
{
isStatusBarTranslucentAndroid: true,
isNavigationBarTranslucentAndroid: true
}
+1 problem is with devices that dont have safeAreaInsets.
I started using it since default Keyboard API cannot detect ribbons (emoji, suggestions...) on Samsung devices with Samsung keyboard since those ribbons are not mounted with keyboard.
So this one works, but flicker is annoying on devices with top/bottom inset 0. I have even condition that but I can still see it:
const {height} = useAnimatedKeyboard({
isStatusBarTranslucentAndroid: safeAreaInsets.top > 0,
isNavigationBarTranslucentAndroid: safeAreaInsets.bottom > 0,
});
problem happens even if I dont use height anywhere.
Reason
When switching screens (e.g., Screen1 → Screen2), if every component using useAnimatedKeyboard() unmounts even briefly, Android falls back to its default keyboard-resize behavior. When the next screen mounts and the hook subscribes again, Reanimated takes back control—resulting in a visible jump/flicker. In this case, Screen1 uses the default resize, and when Screen2 mounts, useAnimatedKeyboard() takes over, causing the flicker.
Fix
Mount a tiny component at the app root that only calls useAnimatedKeyboard() once. This keeps at least one subscriber alive across stack swaps. The rest of your app can keep using useAnimatedKeyboard() as usual — no changes required.
// KeyboardKeeper.tsx
import React from 'react';
import { useAnimatedKeyboard } from 'react-native-reanimated';
// Keeps a global subscription so Android doesn't flip behaviors during screen swaps.
export function KeyboardKeeper({ children }: React.PropsWithChildren) {
useAnimatedKeyboard(); // single, app-wide subscription
return <>{children}</>;
}
// App.tsx (root)
import { NavigationContainer } from '@react-navigation/native';
import { KeyboardKeeper } from './KeyboardKeeper';
export default function App() {
return (
<KeyboardKeeper>
<NavigationContainer>
... app stack
</NavigationContainer>
</KeyboardKeeper>
);
}