Skip to content

iOS 26: default contentInsetAdjustmentBehavior should be scrollableAxes for liquid glass #57299

@IsaacIsrael

Description

@IsaacIsrael

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:

  1. RCTEnhancedScrollView.mminitWithFrame:
  2. RCTScrollViewComponentView.mmupdateProps: (maps the JS default Never → native Never)
  3. RCTScrollViewComponentView.mmprepareForRecycle

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

  1. Run RNTester on an iOS 26+ simulator (Xcode 26 beta)
  2. Ensure UIDesignRequiresCompatibility is not set to YES in Info.plist
  3. Navigate to the Playground screen
  4. 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: Author FeedbackNeeds: ReproThis issue could be improved with a clear list of steps to reproduce the issue.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions