Repository navigation
[iOS] Recycled InputAccessoryView is left visible and blocks touches on its TextInput (0.88 regression from #57590) #59354
Description
Description
On iOS (New Architecture / Fabric), a recycled InputAccessoryView component view is left visible (hidden = NO) and laid out inline on top of its TextInput. It swallows every touch, so the TextInput can no longer be focused: no caret, no keyboard. A freshly created InputAccessoryView is fine; only one dequeued from the recycle pool breaks.
This is a regression in 0.88.0. 0.87.1 is not affected.
Cause (from reading the sources):
RCTInputAccessoryComponentViewhides itself only inupdateProps:(self.hidden = true;).RCTViewComponentView updateLayoutMetrics:passes its stored_layoutMetricstoUIView+ComponentViewProtocol, which doesself.hidden = layoutMetrics.displayType == DisplayType::NonewhenoldLayoutMetrics == EmptyLayoutMetrics(forceUpdate).- fix(ios): reset
EmptyLayoutMetricson Fabric view recycle #57590 (shipped in 0.88) changedRCTViewComponentView prepareForRecyclefrom_layoutMetrics = {};to_layoutMetrics = EmptyLayoutMetrics;. A recycled view now takes theforceUpdatepath on its first layout after reuse, which runs afterupdateProps:on mount and resetshiddentoNO, undoing the accessory view'sself.hidden = true. InputAccessoryComponentDescriptor::adoptmakes the nodeposition: absolutewith the viewport width, so the un-hidden view sits at the parent's top-left, full width, as tall as the accessory content, covering theTextInput.
RCTInputAccessoryComponentView.mm, UIView+ComponentViewProtocol.mm and LayoutMetrics.h are identical between 0.87.1 and 0.88.0; the only differing file on this path is RCTViewComponentView.mm. main still has the same code.
Possible fix (untested): re-assert the hidden state after layout in RCTInputAccessoryComponentView, e.g.
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics
{
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
self.hidden = YES;
[_contentView setFrame:RCTCGRectFromRect(layoutMetrics.getContentFrame())];
}The same reset may affect other component views that hide themselves outside layout.
Real-world impact: any screen or modal with a multiline or number-pad TextInput + InputAccessoryView that is unmounted and mounted again in the same app session. For example, a verification-code field that turns non-editable while submitting (which unmounts/remounts its accessory view) can't be typed into again after a failed attempt.
Steps to reproduce
Using the RNTesterPlayground below (iOS, New Architecture):
- Tap the field. It focuses and the "Done" accessory bar appears. ✅
- Tap Unmount field, then Mount field.
- Tap the field again. Nothing happens: no caret, no keyboard. ❌
Expected: the field focuses every time.
Native inspection after step 2 shows the RCTInputAccessoryComponentView (nativeID playgroundAccessory) with hidden = NO, frame {24, 204, 402, 44}, directly over the TextInput ({25, 205, 352, 42}), and it is the hit-test winner at the field's centre. After step 1 the same view is hidden = YES.
import * as React from 'react';
import {useState} from 'react';
import {Button, InputAccessoryView, StyleSheet, Text, TextInput, View} from 'react-native';
const ACCESSORY_ID = 'playgroundAccessory';
function Playground() {
const [visible, setVisible] = useState(true);
return (
<View style={styles.container}>
<Button
title={visible ? 'Unmount field' : 'Mount field'}
onPress={() => setVisible(v => !v)}
/>
{visible ? (
<View style={styles.field}>
<TextInput
style={styles.input}
keyboardType="number-pad"
inputAccessoryViewID={ACCESSORY_ID}
placeholder="Tap me"
/>
<InputAccessoryView nativeID={ACCESSORY_ID}>
<View style={styles.bar}>
<Text>Done</Text>
</View>
</InputAccessoryView>
</View>
) : null}
</View>
);
}
const styles = StyleSheet.create({
container: {padding: 24},
field: {marginTop: 24},
input: {height: 44, borderWidth: 1, borderColor: '#999', paddingHorizontal: 8},
bar: {height: 44, alignItems: 'flex-end', justifyContent: 'center', paddingHorizontal: 16, backgroundColor: '#eee'},
});React Native Version
0.88.0
Affected Platforms
Runtime - iOS
Output of npx @react-native-community/cli info
System:
OS: macOS 27.0
CPU: (8) arm64 Apple M2
Memory: 90.34 MB / 16.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 24.16.0
Yarn:
version: 1.22.22
npm:
version: 11.13.0
Watchman: Not Found
Managers:
CocoaPods:
version: 1.16.2
SDKs:
iOS SDK:
Platforms:
- DriverKit 27.0
- iOS 27.0
- macOS 27.0
- tvOS 27.0
- visionOS 27.0
- watchOS 27.0
Android SDK: Not Found
IDEs:
Android Studio: 2024.2 AI-242.23726.103.2422.13016713
Xcode:
version: 27.0/27A266a
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.14
path: /usr/bin/javac
Ruby:
version: 2.6.10
path: /usr/bin/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, warning or log output. The TextInput silently ignores taps.
MANDATORY Reproducer
https://gist.github.com/AjayFrancisTechversant/53c0ab6ab267bf31d61b50b133f845ac
Screenshots and Videos
Reproduced on an iPhone 17 Pro simulator (iOS 27.0) with React Native 0.88.0, New Architecture, Hermes. The component tree above was run inside an existing 0.88.0 app rather than inside RNTester itself.
Activity
react-native-bot commentedon Oct 22, 2026
Warning
Missing reproducer: We could not detect a reproducible example in your issue report. Reproducers are mandatory and we can accept only one of those as a valid reproducer:
You can read more about about it on our website: How to report a bug.
[DO NOT MERGE] Repro for react#59354
AjayFrancisTechversant commentedon Oct 22, 2026
Reproducer PR (edited
RNTesterPlayground.js): #59356Steps in RNTester → Playground on iOS (New Architecture): tap the field (works) → Unmount field → Mount field → tap the field again (no focus, no keyboard).