Repository navigation
[iOS] ScrollView automaticallyAdjustKeyboardInsets ignores changes made while the keyboard is visible #59305
Description
Description
On iOS, ScrollView's automaticallyAdjustKeyboardInsets only takes effect when a UIKeyboardWillChangeFrameNotification arrives. If the prop changes while the keyboard is already on screen, nothing happens:
- Turned on while the keyboard is up: no keyboard inset is applied. The end of the content stays behind the keyboard and can't be scrolled into view until the keyboard is hidden and shown again.
- Turned off while the keyboard is up: the keyboard inset stays. When the keyboard is then dismissed, its hide notification is ignored too, since the prop is now off, so the inset is never removed. That leaves a keyboard-sized gap at the end of the content for good.
In RCTScrollViewComponentView.mm, _keyboardWillChangeFrame: returns early when _automaticallyAdjustKeyboardInsets is NO, and updateProps only stores the new value of the flag. Nothing applies or removes the inset when the value changes.
How we hit it: in the Bluesky app, bottom sheets turn automaticallyAdjustKeyboardInsets on only when the sheet reports it's at its full-height detent, because at smaller detents UIKit moves the sheet above the keyboard itself. That report is an event from native, so it reaches the ScrollView's props shortly after the sheet starts presenting. A sheet with an autofocused TextInput raises the keyboard during presentation, before the event lands, so its ScrollView never gets the inset and the user can't scroll to the button at the bottom. Dragging a sheet to full height while typing hits the same gap. More generally, any ScrollView whose automaticallyAdjustKeyboardInsets depends on state that can change while the keyboard is up is affected.
Steps to reproduce
- Clone https://github.com/mozzius/scrollview-keyboard-insets-repro, then
cd ReproducerApp && yarn && (cd ios && bundle install && bundle exec pod install) && yarn ios --mode Release. - A: with the
automaticallyAdjustKeyboardInsetsSwitch off, tap the text input. Turn the Switch on (the keyboard stays up), then scroll to the bottom. - B: with the Switch on, tap the text input. Turn the Switch off, press Return to dismiss the keyboard, then scroll to the bottom.
The readout under the Switch shows the keyboard height and the contentInset.bottom from the last onScroll event.
Expected: A gets the keyboard inset (335pt here) and the yellow "End of content" block scrolls into view above the keyboard. In B, the inset goes back to 0 once the keyboard is gone.
Actual:
contentInset.bottom |
||
|---|---|---|
| Control: prop on before the keyboard appears | 335 | works |
| A: prop turned on with the keyboard up | 0 | end of the content stuck behind the keyboard |
| B: prop turned off with the keyboard up, then keyboard hidden | 335 | permanent blank gap under the content |
Possible fix
Keep the keyboard's end frame from every notification, including those that arrive while the prop is off. When updateProps sees the prop change while the keyboard is visible, apply the inset as if the keyboard had just appeared, or remove it and clamp the offset. The repro's fixed branch applies that as a patch (RN built from source): A gets 335, B goes back to 0, and flipping the prop while the keyboard is hidden changes nothing. PR: #59306
Related but different: #56189 (stale gap after dismissal with contentInsetAdjustmentBehavior="automatic") and #57755 (the flag surviving view recycling).
React Native Version
0.88.0
Affected Platforms
Runtime - iOS, Build - MacOS
Output of npx @react-native-community/cli info
System:
OS: macOS 27.0.1
CPU: (14) arm64 Apple M4 Pro
Memory: 2.34 GB / 48.00 GB
Shell:
version: 5.3.20
path: /opt/homebrew/bin/bash
Binaries:
Node:
version: 24.19.0
path: /Users/samuel/.nvm/versions/node/v24.19.0/bin/node
Yarn:
version: 1.22.22
path: /Users/samuel/.nvm/versions/node/v24.19.0/bin/yarn
npm:
version: 11.17.0
path: /Users/samuel/.nvm/versions/node/v24.19.0/bin/npm
Watchman:
version: 2026.10.19.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.17.0
path: /Users/samuel/.rbenv/shims/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 27.0
- iOS 27.1
- macOS 27.0
- tvOS 27.0
- visionOS 27.0
- watchOS 27.0
Android SDK:
API Levels:
- "29"
- "33"
- "34"
- "35"
- "36"
- "37"
Build Tools:
- 30.0.3
- 34.0.0
- 35.0.0
- 35.0.1
- 36.0.0
- 37.0.0
System Images:
- android-28 | Google ARM64-V8a Play ARM 64 v8a
- android-29 | Google Play ARM 64 v8a
- android-30 | Google APIs ARM 64 v8a
- android-34 | Google Play ARM 64 v8a
- android-35 | Google Play ARM 64 v8a
- android-35 | Google Play Tablet ARM 64 v8a
- android-36 | Google Play ARM 64 v8a
Android NDK: Not Found
IDEs:
Android Studio: 2026.1 AI-261.26222.65.2614.16379836
Xcode:
version: 27.1/27A9275
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.20.1
path: /usr/bin/javac
Ruby:
version: 2.7.6
path: /Users/samuel/.rbenv/shims/ruby
npmPackages:
"@react-native-community/cli":
installed: 20.2.0
wanted: 20.2.0
react:
installed: 19.3.0
wanted: 19.3.0
react-native:
installed: 0.88.0
wanted: 0.88.0
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: true
iOS:
hermesEnabled: true
newArchEnabled: true
Stacktrace or Logs
No crash or error. The ScrollView's contentInset.bottom (from onScroll) stays at 0 after the prop is turned on with the keyboard up, and stays at the keyboard height (335) after the prop is turned off and the keyboard is dismissed.
MANDATORY Reproducer
https://github.com/mozzius/scrollview-keyboard-insets-repro
Screenshots and Videos
iPhone 17 simulator, iOS 26.5, Release.
| A, stock | A, fixed | B, stock | B, fixed |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |




Activity