Description
On iOS 26+, Apple introduced the "liquid glass" design language with translucent system chrome (tab bars, toolbars, navigation bars). For scroll views to properly adjust their content to account for this translucent chrome, contentInsetAdjustmentBehavior should default to UIScrollViewContentInsetAdjustmentScrollableAxes instead of UIScrollViewContentInsetAdjustmentNever.
Currently, React Native hardcodes UIScrollViewContentInsetAdjustmentNever in three places:
RCTEnhancedScrollView.mm — initWithFrame:
RCTScrollViewComponentView.mm — updateProps: (maps the JS default Never → native Never)
RCTScrollViewComponentView.mm — prepareForRecycle
This means scroll views (ScrollView, FlatList, SectionList, etc.) do not automatically add content insets for translucent system chrome on iOS 26+. Content scrolls behind the tab bar with no way for users to see the last items without manually adding bottom padding on every screen.
Proposed Solution
On iOS 26+, when UIDesignRequiresCompatibility is not set to YES in the app's Info.plist:
- Default
contentInsetAdjustmentBehavior to UIScrollViewContentInsetAdjustmentScrollableAxes in initWithFrame:
- In
updateProps:, upgrade the incoming Never value (the JS default) to ScrollableAxes
- In
prepareForRecycle, reset to ScrollableAxes instead of Never
This matches Apple's intended behavior for iOS 26 apps using the new design language. Apps that explicitly set contentInsetAdjustmentBehavior to "automatic", "always", or "scrollableAxes" from JS are unaffected. Apps with UIDesignRequiresCompatibility = YES (opting out of liquid glass) retain the current Never default.
Reproducer
Edit RNTesterPlayground.js with the following:
import React from 'react';
import {FlatList, Text, View, StyleSheet} from 'react-native';
const DATA = Array.from({length: 50}, (_, i) => ({
id: String(i),
title: `Item ${i + 1}`,
}));
export default function Playground() {
return (
<FlatList
data={DATA}
keyExtractor={item => item.id}
contentInsetAdjustmentBehavior="scrollableAxes"
renderItem={({item}) => (
<View style={styles.item}>
<Text style={styles.text}>{item.title}</Text>
</View>
)}
/>
);
}
const styles = StyleSheet.create({
item: {
padding: 20,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#ccc',
},
text: {fontSize: 16},
});
Steps
- Run RNTester on an iOS 26+ simulator (Xcode 26 beta)
- Ensure
UIDesignRequiresCompatibility is not set to YES in Info.plist
- Navigate to the Playground screen
- Scroll to the bottom of the list
Observed behavior
Even with contentInsetAdjustmentBehavior="scrollableAxes" set explicitly, RCTScrollViewComponentView.mm updateProps: maps the JS value to the native enum but the initial Never set in RCTEnhancedScrollView.initWithFrame: and the recycling reset in prepareForRecycle override it, so the last items in the list are hidden behind the translucent tab bar / home indicator area with no automatic inset adjustment.
When no contentInsetAdjustmentBehavior prop is set (the common case), the default Never is sent, which is hardcoded and cannot be overridden by the system.
Expected behavior
On iOS 26+ without UIDesignRequiresCompatibility, scroll views should default to ScrollableAxes so content automatically insets for translucent system chrome, matching native UIKit behavior.
Note: This requires an iOS 26+ simulator (Xcode 26 beta). A Snack reproducer is not feasible because Snack does not support iOS 26.
Environment
- React Native: 0.85.x
- iOS: 26.x (Xcode 26)
- Architecture: New Architecture (Fabric)
Description
On iOS 26+, Apple introduced the "liquid glass" design language with translucent system chrome (tab bars, toolbars, navigation bars). For scroll views to properly adjust their content to account for this translucent chrome,
contentInsetAdjustmentBehaviorshould default toUIScrollViewContentInsetAdjustmentScrollableAxesinstead ofUIScrollViewContentInsetAdjustmentNever.Currently, React Native hardcodes
UIScrollViewContentInsetAdjustmentNeverin three places:RCTEnhancedScrollView.mm—initWithFrame:RCTScrollViewComponentView.mm—updateProps:(maps the JS defaultNever→ nativeNever)RCTScrollViewComponentView.mm—prepareForRecycleThis means scroll views (ScrollView, FlatList, SectionList, etc.) do not automatically add content insets for translucent system chrome on iOS 26+. Content scrolls behind the tab bar with no way for users to see the last items without manually adding bottom padding on every screen.
Proposed Solution
On iOS 26+, when
UIDesignRequiresCompatibilityis not set toYESin the app's Info.plist:contentInsetAdjustmentBehaviortoUIScrollViewContentInsetAdjustmentScrollableAxesininitWithFrame:updateProps:, upgrade the incomingNevervalue (the JS default) toScrollableAxesprepareForRecycle, reset toScrollableAxesinstead ofNeverThis matches Apple's intended behavior for iOS 26 apps using the new design language. Apps that explicitly set
contentInsetAdjustmentBehaviorto"automatic","always", or"scrollableAxes"from JS are unaffected. Apps withUIDesignRequiresCompatibility = YES(opting out of liquid glass) retain the currentNeverdefault.Reproducer
Edit
RNTesterPlayground.jswith the following:Steps
UIDesignRequiresCompatibilityis not set toYESin Info.plistObserved behavior
Even with
contentInsetAdjustmentBehavior="scrollableAxes"set explicitly,RCTScrollViewComponentView.mmupdateProps:maps the JS value to the native enum but the initialNeverset inRCTEnhancedScrollView.initWithFrame:and the recycling reset inprepareForRecycleoverride it, so the last items in the list are hidden behind the translucent tab bar / home indicator area with no automatic inset adjustment.When no
contentInsetAdjustmentBehaviorprop is set (the common case), the defaultNeveris sent, which is hardcoded and cannot be overridden by the system.Expected behavior
On iOS 26+ without
UIDesignRequiresCompatibility, scroll views should default toScrollableAxesso content automatically insets for translucent system chrome, matching native UIKit behavior.Environment