Repository navigation
Keyboard Flickering on TextInput with secureTextEntry #39411
Description
Activity
- addedComponent: TextInputRelated to the TextInput component.Related to the TextInput component.
on Sep 12, 2023 - addedIssue: Author Provided ReproThis issue can be reproduced in Snack or an attached project.This issue can be reproduced in Snack or an attached project.
on Sep 14, 2023 I also encountered the similar issue when in login screen
Reacted by Osman YILMAZ, Alex Shaw, YuriMooveup, Badre Daha Belghiti, vkondin, VasylRohozha, Ario99, sumith s nair, Ebrahim Abdollahian, ushyDev and 18 moreI am experiencing the same on login screens. It's a newer issue. It seems to be caused by the suggested autofill.
The problem is reduce by using default value prop passed to TextInput component that holds its own text ref and passes out ref value on change.
Reacted by JL Granda and MedkhatI figured out the problem
It appears to be that at some point recently (pre iOS 17) a bug was introduced. Whenever
secureTextEntry={false}andtextContentTypeis set, various Text Input changes (typing, focus, etc...) will fire a nativekeyboardWillChangeFrameevent that causes the keyboard to re-render. This also causes iOS'sAutoFill QuickType Bar(forgive me if my terminology is incorrect) to flicker.This can be especially problematic for any RN apps using
KeyboardAvoidingViewwith iOSbehavior={'position'}since it will cause a layout flicker as it adjusts to the disappearing and reappearingAutoFill QuickType Bar.You can actually see the issue in native iOS apps:
- Go to the login screen of a native iOS app.
- Change focus from username and password fields.
- In many apps you can see a slight bounce as the keyboard animations handle the
AutoFill QuickType Barflicker.
For those who are using KeyboardAvoidingView (RN V0.72.* Patch Fix)
For those who are wrapping their text inputs in a
<KeyboardAvoidingView behavior= 'position'}>and<ScrollView>, you are likely dealing with your screen jumping on text change and focus change.This isn't a pretty solution (and keep in mind it does not fix the root issue of the keyboard re-render), but you can do the following
**TEMPORARY and NOT fully tested**fix for this issue:- Go to the file at:
node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.js - Add
ignoreIOSKeyboardWillChangeEvents?: boolean | undefined;to the KeyboardAvoidingView props. - Go to the types file at:
node_modules/react-native/Libraries/Components/Keyboard/KeyboardAvoidingView.d.ts - Add
ignoreIOSKeyboardWillChangeEvents?: boolean | undefined;to the KeyboardAvoidingView props. - Replace the
const {duration, easing, endCoordinates} = this._keyboardEvent;line with the following:
const {duration, easing, endCoordinates} = this._keyboardEvent; if(Platform.OS === 'ios' && duration === 0 && !!this.props.ignoreIOSKeyboardWillChangeEvents ) { return; }- Use patch package to save the changes.
be sure to set
ignoreIOSKeyboardWillChangeEvents={true}with care. I, for example, am only using it on a login screen for a very specific keyboard avoiding use case scenario that I detail above. Hope this helps.Reacted by ㅤ, HB, Jeff Lewis, Adrian Solarczyk, MobileDeveloper, Ian, yungarium, kai, Feng Yu, Vedad Burgic and 13 moreReacted by Dr. Jose L. Salazar, Jeff Lewis, Tien Huu Nguyen, Taylor Ellis, kyle-ts and Maciej SzostakRPReplay-Final1696326197.mov
I began seeing this after updating the device i test on (iPhone 14 Pro) to iOS 17. Using Expo Go. Very annoying...
What's interesting is that I am not experiencing this on another iPhone 11 Pro Max, also running iOS 17.
Reacted by Benji Soar, Paulius Rimgaila, VasylRohozha, Ario99, Evan Sendra, Ion PANTELEICIUC, Siddharth Agarwal, RJ, Inácio Pires, Steven MacCoun and 2 more- Reacted by Benji Soar, m_kimura, Nick Parsons, Laurent, Ruan, David Axelrod, vkondin, VasylRohozha, Ario99, Yehonatan Tsirolnik and 19 more
I can confirm that it is an issue with the Passwords option in the Keyboard toolbar. Seems to be an issue across iOS 17 rather than a ReactNative issue. (It also isn't fixed in iOS 17.1, so we may be waiting a while for a fix).
Closing as it isn't a React Native-specific issue.
Reacted by Thor Anderberg Nilsson, Shadrac Reyes, Matt Race, Marius, Albert, m_kimura, Laszlo Kocsis, Ian, Yehonatan Tsirolnik, JL Granda and 4 moreReacted by Anand Kumar, T.J, Andrei and Psi QuestReacted by Caio Teixeira, harish-narra, Seb Smith, Osman YILMAZ and chizirumizuhara269-rgbAdding
textContentType='oneTimeCode'to your TextInput disables the Password toolbar, which also eliminates the described issue.Reacted by Neil Buckley, Enoc Montiel, sanespi09, Matt Race, harish-narra, Osman YILMAZ, Elias Sommer, Noah Kurz, Marcus Moore, DanilKasianenko and 75 moreReacted by Stewan Silva, Daniel Prado, gayanehormona, mtch99, yevhen2025, Elizabeth Manrique, P and Lars DanauReacted by ASHIRKHAN P M, Jaydeep Purohit, Talah Zameer, AteeqAdnan, rblcb, Medkhat, Muhammad Hasan, Psi Quest and Daniel Sansão AraldiReacted by ASHIRKHAN P M, Taya Ugay, Jaydeep Purohit, Talah Zameer, Keshav Khetan, Alexis Ortiz, AteeqAdnan, Gustavo G. Kozoroski, dgallego, Armaan Mandlaus and 16 moreReacted by ASHIRKHAN P M, Jaydeep Purohit, Talah Zameer, AteeqAdnan, Amar Meena, rblcb, dcaceres7, Medkhat, Muhammad Hasan, Brayner Felipe and 7 more@ThorANilsson I tried this, but it's not working for me.
Here's what I did.
TL;DR: If it's a password field, on each change update the actual string value in a map, and then render the value as bullets (
'•'). What we're essentially doing here is saying that all of our fields are just regular fields without autofill, but we can manipulate the value of the field, if it's a password field, if we pass a certain flag.Somewhere created a file called
maskMap.tswith the following:export const maskMap = new Map<string, string>();
And in your custom
TextFieldcomponent (I'm assuming most of you have wrappedTextInputinto one of your ownTextFieldcomponents):import * as React from 'react'; import { TextInput } from 'react-native-paper'; import { maskMap } from './maskMap'; const PASSWORD_MASK_CHAR = '•'; . . . // secureTextEntry determines whether or not this is a password field const TextField = React.memo(({ onChange, secureTextEntry, ...props }: YourTextFieldProps) => { const [textFieldId] = React.useState(uuid()); // use whatever uuid generator you'd like . . . React.useEffect(() => { maskMap.set(textFieldId, ''); return () => { maskMap.delete(textFieldId); }; }, [secureTextEntry]); const handleChange = React.useCallback((e: string) => { if (secureTextEntry) { const previousValue = maskMap.get(textFieldId) as string; const formattedNewValue = e.replace(new RegExp(PASSWORD_MASK_CHAR, 'g'), ''); const newValue = previousValue + formattedNewValue; onChange(newValue); maskMap.set(textFieldId, previousValue + formattedNewValue); } else { onChange(e); } }, [secureTextEntry]); const getValue = React.useCallback(() => { if (!secureTextEntry) { return value; } return value.replaceAll(/./g, PASSWORD_MASK_CHAR); }, [value, secureTextEntry, showText, maskMap]); . . . return ( . . . <TextInput value={getValue()} onChangeText={handleChange} textContentType='none' // add this . . . /> . . . ) })
Reacted by AlexReacted by juce, Егор Скляров, tonigue, ntphu1808, André, Mokhtar25, RJ, Waldry Diaz, Bo Bramer, Miguel Jara and 8 morehow this can be closed if its still happening ?
it happends all the time when you have
textContentType="password"orsecureTextEntryReacted by Luis Barrientos Fajardo, Shen, Picard P., Power Snail and Daniel Israel84 remaining items
This Flutter issue also is about this iOS bug and apparently they recently fixed this. Looks like they set
autoCorrectto false when username or password related autofill hints are enabled. I've also been debugging this issue for some time and addingautoCorrect={false}to my username input also fixed the flickering and UI shifting. Still not a real fix, because it is an iOS issue.Reacted by Jon Lovemaybe this will help someone but I had this nightmare of an issue for 3 days straight tried everything but the solution was to remove any unmounted effects that were left from other screens I was pushing from. The fix in my case was that instead of using .push() I used .replace() this unmounted any unattended effects that were causing the screen to flicker. Seriously as soon as I replaced it it worked flawlessly.
Reacted by JesusHello folks 👋
I also ran into this issue and wanted to preserve the ability to show the "Passwords" autofill from iOS.
I couldn't find any relevant workarounds online, so I ended up vibe coding my own
KeyboardAvoidingViewbased on the React Native one (behavior="padding").It's essentially the same code but with debouncing added 👍
It's working for me now, and I hope it can help others!
With the react-native
KeyboardAvoidingView:keyboardavoidingview_rvz0jTXS.mov
With the new
KeyboardPasswordsAvoidingView:keyboardpasswordsavoidingview_sNcU7REh.mov
import { useEffect, useRef, useState } from "react"; import { AccessibilityInfo, Keyboard, LayoutAnimation, Platform, KeyboardAvoidingView as RNKeyboardAvoidingView, View, ViewProps, } from "react-native"; interface KeyboardMetrics { screenX: number; screenY: number; width: number; height: number; } interface KeyboardEvent { duration?: number; easing?: string; endCoordinates: KeyboardMetrics; } interface ViewLayout { x: number; y: number; width: number; height: number; } interface KeyboardPasswordsAvoidingViewProps extends ViewProps { enabled?: boolean; keyboardVerticalOffset?: number; } const DEBOUNCE_DELAY = 50; // ms /** * Custom KeyboardAvoidingView specifically designed to handle iOS password * autofill bar flicker issues. Uses debouncing to prevent layout jumps * caused by rapid keyboard frame changes when iOS shows/hides the password bar. * * Based on React Native's KeyboardAvoidingView with debouncing logic. * iOS only - falls back to regular View on other platforms. * Padding behavior only. */ export const KeyboardPasswordsAvoidingView = ({ enabled = true, keyboardVerticalOffset = 0, style, onLayout, children, ...props }: KeyboardPasswordsAvoidingViewProps) => { const [bottom, setBottom] = useState(0); const frameRef = useRef<ViewLayout | null>(null); const keyboardEventRef = useRef<KeyboardEvent | null>(null); const debounceTimeoutRef = useRef<NodeJS.Timeout | null>(null); const isKeyboardVisibleRef = useRef<boolean>(false); const relativeKeyboardHeight = async ( keyboardFrame: KeyboardMetrics, ): Promise<number> => { const frame = frameRef.current; if (!frame || !keyboardFrame) { return 0; } // Handle iOS Prefer Cross-Fade Transitions accessibility setting if ( Platform.OS === "ios" && keyboardFrame.screenY === 0 && (await AccessibilityInfo.prefersCrossFadeTransitions()) ) { return 0; } const keyboardY = keyboardFrame.screenY - keyboardVerticalOffset; // Calculate the displacement needed for the view such that it // no longer overlaps with the keyboard (padding behavior only) return Math.max(frame.y + frame.height - keyboardY, 0); }; const updateBottomIfNecessary = async () => { if (!keyboardEventRef.current) { setBottom(0); return; } const { duration, easing, endCoordinates } = keyboardEventRef.current; const height = await relativeKeyboardHeight(endCoordinates); if (bottom === height) { return; } setBottom(height); // Apply layout animation if enabled and duration is provided if (enabled && duration && easing) { LayoutAnimation.configureNext({ duration: duration > 10 ? duration : 10, update: { duration: duration > 10 ? duration : 10, type: (LayoutAnimation.Types as any)[easing] || "keyboard", }, }); } }; const debouncedUpdateBottom = () => { // Clear existing timeout if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); } // Debounce the update debounceTimeoutRef.current = setTimeout(() => { updateBottomIfNecessary(); }, DEBOUNCE_DELAY); }; const onKeyboardChange = (event: KeyboardEvent | null) => { keyboardEventRef.current = event; // Filter out problematic events with zero duration (common cause of flicker) if (event && event.duration === 0) { return; } isKeyboardVisibleRef.current = true; debouncedUpdateBottom(); }; const onKeyboardDidShow = (event: KeyboardEvent | null) => { // Only trigger if keyboard was already visible - this indicates a focus change if (isKeyboardVisibleRef.current && event) { keyboardEventRef.current = event; // Use debounced update for focus changes to prevent visual glitch debouncedUpdateBottom(); } }; const onKeyboardHide = () => { keyboardEventRef.current = null; isKeyboardVisibleRef.current = false; debouncedUpdateBottom(); }; const handleLayout = async (event: any) => { const oldFrame = frameRef.current; frameRef.current = event.nativeEvent.layout; // Update bottom height for the first time or when the height changes if ( !oldFrame || (frameRef.current && oldFrame.height !== frameRef.current.height) ) { await updateBottomIfNecessary(); } if (onLayout) { onLayout(event); } }; useEffect(() => { // Only setup listeners on iOS if (Platform.OS !== "ios") { return; } // Initialize keyboard state const keyboardVisible = Keyboard.isVisible(); isKeyboardVisibleRef.current = keyboardVisible; if (!keyboardVisible) { keyboardEventRef.current = null; setBottom(0); } // Setup iOS keyboard listeners const subscriptions = [ // iOS uses keyboardWillShow/keyboardWillHide for better handling // of undocked, split, or floating keyboards Keyboard.addListener("keyboardWillHide", onKeyboardHide), Keyboard.addListener("keyboardWillShow", onKeyboardChange), // Listen for keyboardDidShow to detect focus changes Keyboard.addListener("keyboardDidShow", onKeyboardDidShow), ]; return () => { // Clear debounce timeout on cleanup if (debounceTimeoutRef.current) { clearTimeout(debounceTimeoutRef.current); } // Remove event listeners subscriptions.forEach(subscription => { subscription.remove(); }); }; }, []); // On non-iOS platforms, just return the regular KeyboardAvoidingView if (Platform.OS !== "ios") { return ( <RNKeyboardAvoidingView style={style} keyboardVerticalOffset={keyboardVerticalOffset} {...props} > {children} </RNKeyboardAvoidingView> ); } // Apply padding bottom based on keyboard height const bottomHeight = enabled ? bottom : 0; return ( <View style={[style, { paddingBottom: bottomHeight }]} onLayout={handleLayout} {...props} > {children} </View> ); };
and then you can use it just like a basic
KeyboardAvoidingView:<KeyboardPasswordsAvoidingView keyboardVerticalOffset={20}> <SignupForm /> </KeyboardPasswordsAvoidingView>
Reacted by Krzysztof Motas, Tanmoy Sarkar, Priyadarshan Sarkar, Alexander Khodyrev and Zülal NebinStill waiting on this issue being properly resolved. I use this patch and update it every time I upgrade to the next react-native version.
@ShadReyes thanks for the solution! Would you happen to be using expo sdk 53? I started to upgrade from expo sdk 52 but haven't been able to properly test it yet.
Reacted by Diego Sanmartin@ShadReyes thanks for the solution! Would you happen to be using expo sdk 53? I started to upgrade from expo sdk 52 but haven't been able to properly test it yet.
@Ario99 I stopped contracting for the company that had the problem described in this issue, so I have not had a chance to test in on Expo 53. Though, I would say it is unlikely that the new Expo version would fix the issues. This is a problem stemming from a native iOS bug that was introduced a little before iOS 17. Though, when I get a second, I can spin up a test app and test it out.
In my RN project version
0.79.3, using iOS 18.7, this problem persists.Reacted by Denys SavinI can confirm that is also related to Passwords app suggestion on my side as well. When I disable Password & Autofill service in settings everything works properly. For now I have set
textContentType="oneTimeCode"for some of the text inputs in the app.Simulator.Screen.Recording.-.iPhone.16.-.2025-11-09.at.21.30.22.mov
- added 2 commits that reference this issue
on Jan 22, 2026 @ThorANilsson I tried this, but it's not working for me.
Yes, that didn't work for me either.
Having the same issue, no resolutions yet using Expo SDK
54using react-native-keyboard-controller.Reacted by Alexander Polyanov and Denys SavinIssue still persists with latest IOS and Expo SDK
@apolyanov Flicker was triggered due to autofill in secure entry field. Below is how I resolved the same in my app.
Set
textContentType="none"andautoCorrect={false}on theTextInputto prevent iOS from treating the fields as login credentials and showing the auto-fill bar:<TextInput placeholder="Username" textContentType="none" autoCorrect={false} // ... other props /> <TextInput placeholder="Password" secureTextEntry textContentType="none" autoCorrect={false} // ... other props />

Description
If you have 2 text inputs, (One with secure text entry enabled and the other set to disabled), When typing, highlighting and scrolling in the non-secure text entry, the keyboard will flicker.
This is an iOS issue only, I have tested this using iOS 17, on an iPhone 13 mini. I cannot repeat this issue in the simulator, only on the device.
It appears to be when iOS recognizes it as a login field, so it presents the auto-fill passwords UI. See the attached video on the flickering issue described.
RPReplay_Final1694522683.mov
React Native Version
0.72.4
Output of
npx react-native infoSystem:
OS: macOS 13.4.1
CPU: (10) arm64 Apple M1 Pro
Memory: 115.16 MB / 16.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 20.3.1
path: /opt/homebrew/bin/node
Yarn:
version: 1.22.19
path: /usr/local/bin/yarn
npm:
version: 9.6.7
path: /opt/homebrew/bin/npm
Watchman:
version: 2023.06.12.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.11.3
path: /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 22.4
- iOS 16.4
- macOS 13.3
- tvOS 16.4
- watchOS 9.4
Android SDK:
API Levels:
- "28"
- "31"
- "32"
- "33"
Build Tools:
- 30.0.2
- 30.0.3
- 32.0.0
- 33.0.0
- 34.0.0
System Images:
- android-30 | Google Play ARM 64 v8a
- android-33 | Google APIs ARM 64 v8a
- android-33 | Google Play ARM 64 v8a
- android-34 | Google APIs ARM 64 v8a
Android NDK: Not Found
IDEs:
Android Studio: 2022.1 AI-221.6008.13.2211.9619390
Xcode:
version: 14.3.1/14E300b
path: /usr/bin/xcodebuild
Languages:
Java:
version: 18.0.1.1
path: /usr/bin/javac
Ruby:
version: 2.6.10
path: /usr/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 18.2.0
wanted: 18.2.0
react-native:
installed: 0.72.4
wanted: 0.72.4
react-native-macos: Not Found
npmGlobalPackages:
"react-native": Not Found
Android:
hermesEnabled: true
newArchEnabled: false
iOS:
hermesEnabled: true
newArchEnabled: false
Steps to reproduce
Have 2 TextInput components inside a view, one set with secureTextEntry, one without.
This issue is only seen on device, not in simulator
Snack, screenshot, or link to a repository
https://github.com/gcon97/KeyboardFlickeringDemo