Describe the bug
After enabling KeyboardProvider with navigationBarTranslucent, Android safe-area/window insets change unexpectedly over time on Android 15+ (API 35+) in Landscape mode.
Code snippet
import React, { useEffect } from 'react'
import { StyleSheet, Platform } from 'react-native'
import {
SafeAreaProvider,
SafeAreaView,
useSafeAreaInsets
} from 'react-native-safe-area-context'
import { KeyboardProvider } from 'react-native-keyboard-controller'
const RootInsetsLogger = () => {
const insets = useSafeAreaInsets()
useEffect(() => {
console.log('[App] safe area insets', insets)
}, [insets])
return null
}
export default function App() {
return (
<SafeAreaProvider>
<RootInsetsLogger />
<KeyboardProvider navigationBarTranslucent>
<SafeAreaView
edges={Platform.OS === 'ios' ? [] : ['bottom', 'left', 'right']}
style={styles.container}
/>
</KeyboardProvider>
</SafeAreaProvider>
)
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'black' }
})
Observed behavior
Insets reported by useSafeAreaInsets() transition through multiple states during startup/runtime, for example:
[App] safe area insets {
"left": 0,
"bottom": 0,
"right": 48,
"top": 24
}
[App] safe area insets {
"left": 0,
"bottom": 0,
"right": 48,
"top": 0
}
[App] safe area insets {
"left": 0,
"bottom": 0,
"right": 0,
"top": 24
}
Expected behavior
With edge-to-edge enabled, insets should be stable and predictable for layout usage, or there should be clear guidance on expected transient inset states and when values are considered final.
Environment
- Android 15+ (API 35+)
- React Native 0.79.3
- react-native-keyboard-controller 1.21.8
- react-native-safe-area-context 5.4.0
- Hermes enabled
Additional context
On Android 14 and lower, everything works correctly and inset values remain stable.
The problem appears on Android 15+ only, where insets change during runtime after enabling KeyboardProvider edge-to-edge behavior.
This inset change is what surfaces the issue in react-native-modal: the modal can overlap and render under the navigation bar.
So this is version-dependent behavior: not reproducible on older Android versions, reproducible on Android 15+.
Also, my layout depends on useSafeAreaInsets() values (especially left/right insets) to calculate horizontal padding, so inset changes directly affect modal/content horizontal spacing and positioning
Screenshots
Android 15
Android 14
Workaround: wrapping modal content in its own SafeAreaView resolves the overlap on Android 15+ in my app setup.
Without that modal-local SafeAreaView, modal content can render under the navigation bar when KeyboardProvider edge-to-edge is enabled.
In my case, the workaround became reliable after I removed manual inset-based layout handling from useSafeAreaInsets and instead wrapped the component with SafeAreaView and relied on SafeAreaView edges to handle insets.
This suggests inset handling differs between root content and modal surface on Android 15+, even when logged inset values look similar.
On Android 14 and below, this behavior is not reproducible in the same setup.
<Modal
isVisible={showModal}
style={styles.modalStyle}
animationIn="slideInUp"
backdropTransitionInTiming={650}
backdropTransitionOutTiming={0}
backdropOpacity={1}
backdropColor="yellow"
statusBarTranslucent
>
<SafeAreaView
edges={Platform.OS === 'ios' ? [] : ['bottom', 'left', 'right']}
style={{ flex: 1, justifyContent: 'flex-end' }}
>
{Content}
</SafeAreaView>
</Modal>

Android 15 after wraping the Modal content with SafeAreaView
Describe the bug
After enabling KeyboardProvider with navigationBarTranslucent, Android safe-area/window insets change unexpectedly over time on Android 15+ (API 35+) in Landscape mode.
Code snippet
Observed behavior
Insets reported by useSafeAreaInsets() transition through multiple states during startup/runtime, for example:
Expected behavior
With edge-to-edge enabled, insets should be stable and predictable for layout usage, or there should be clear guidance on expected transient inset states and when values are considered final.
Environment
Additional context
On Android 14 and lower, everything works correctly and inset values remain stable.
The problem appears on Android 15+ only, where insets change during runtime after enabling KeyboardProvider edge-to-edge behavior.
This inset change is what surfaces the issue in react-native-modal: the modal can overlap and render under the navigation bar.
So this is version-dependent behavior: not reproducible on older Android versions, reproducible on Android 15+.
Also, my layout depends on useSafeAreaInsets() values (especially left/right insets) to calculate horizontal padding, so inset changes directly affect modal/content horizontal spacing and positioning
Screenshots
Workaround: wrapping modal content in its own SafeAreaView resolves the overlap on Android 15+ in my app setup.
Without that modal-local SafeAreaView, modal content can render under the navigation bar when KeyboardProvider edge-to-edge is enabled.
In my case, the workaround became reliable after I removed manual inset-based layout handling from useSafeAreaInsets and instead wrapped the component with SafeAreaView and relied on SafeAreaView edges to handle insets.
This suggests inset handling differs between root content and modal surface on Android 15+, even when logged inset values look similar.
On Android 14 and below, this behavior is not reproducible in the same setup.