Skip to content

fix(textinput): correct placeholder layout constraints (px vs DIP) and no-op NaN fontSize guard - #16317

Open
FaithfulAudio wants to merge 3 commits into
microsoft:mainfrom
FacilitronWorks:fix/textinput-placeholder-layout-main
Open

fix(textinput): correct placeholder layout constraints (px vs DIP) and no-op NaN fontSize guard#16317
FaithfulAudio wants to merge 3 commits into
microsoft:mainfrom
FacilitronWorks:fix/textinput-placeholder-layout-main

Conversation

@FaithfulAudio

@FaithfulAudio FaithfulAudio commented Jul 18, 2026

Copy link
Copy Markdown

Forward-port of #16303 (which targets 0.83-stable) to main, per @acoates-ms:

yeah, we should get a main check-in too otherwise it wont make it into future releases.

Both defects are present on main today, in WindowsTextInputComponentView::CreatePlaceholderLayout().

1. Placeholder layout constraints were fed physical pixels, not DIPs

m_imgWidth/m_imgHeight are physical pixels (frame * pointScaleFactor), but facebook::react::LayoutConstraints are expressed in DIPs. The placeholder was therefore laid out inside a box pointScaleFactorx too large, so it measured and positioned at a different height than the typed text — visible as the placeholder sitting at a different baseline than the value the user types, and worsening as scale increases.

-  constraints.maximumSize.width = static_cast<FLOAT>(m_imgWidth);
-  constraints.maximumSize.height = static_cast<FLOAT>(m_imgHeight);
+  constraints.maximumSize.width = static_cast<FLOAT>(m_imgWidth) / m_layoutMetrics.pointScaleFactor;
+  constraints.maximumSize.height = static_cast<FLOAT>(m_imgHeight) / m_layoutMetrics.pointScaleFactor;

This version divides directly, with no != 0 guard — per review feedback on #16303. LayoutMetrics::pointScaleFactor has a default member initializer of 1.0 and EmptyLayoutMetrics only designates .frame, so even the pre-layout sentinel inherits 1.0; there is no path here where it is 0.

2. The NaN fontSize guard was a no-op

The guard evaluated defaultTextAttributes().fontSize as a discarded expression statement rather than assigning it, so a placeholder with no explicit fontSize never actually picked up the default:

   if (std::isnan(props.textAttributes.fontSize)) {
-    facebook::react::TextAttributes::defaultTextAttributes().fontSize;
+    textAttributes.fontSize = facebook::react::TextAttributes::defaultTextAttributes().fontSize;
   }

Testing

Verified on the 0.83-stable version of this change (#16303): compiled into a framework source build (binary-gated) and re-probed in a production app at 250% scale — no regression, the NaN-fontSize guard now actually assigns (a no-fontSize placeholder renders at the default size), and placeholder-vs-typed are pixel-comparable at that geometry. Evidence attached to #16303.

This main port is the identical change applied to the current main source; it has not been separately run on-device, so CI is the gate here.

Related: #16303 (0.83-stable).

Microsoft Reviewers: Open in CodeFlow

…d no-op NaN fontSize guard

Forward-port of microsoft#16303 (0.83-stable) to main.

CreatePlaceholderLayout fed m_imgWidth/m_imgHeight - which are physical
pixels (frame * pointScaleFactor) - into LayoutConstraints, which are
expressed in DIPs. The placeholder was laid out in a box pointScaleFactor
times too large, so it measured and positioned at a different height than
the typed text. Divide by pointScaleFactor.

The NaN fontSize guard was also a no-op: it evaluated
defaultTextAttributes().fontSize as a discarded expression statement
instead of assigning it, so a placeholder with no fontSize never picked
up the default.
@FaithfulAudio
FaithfulAudio requested a review from a team as a code owner July 18, 2026 11:54
@azure-pipelines

Copy link
Copy Markdown
Contributor
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR forward-ports a TextInput placeholder rendering fix to main by correcting unit mismatches (physical pixels vs DIPs) in placeholder layout constraints and fixing a no-op NaN fontSize fallback so placeholders reliably use the default font size.

Changes:

  • Convert placeholder LayoutConstraints.maximumSize from physical pixels (m_imgWidth/m_imgHeight) to DIPs by dividing by m_layoutMetrics.pointScaleFactor.
  • Fix the NaN fontSize guard to actually assign the default fontSize into the placeholder’s TextAttributes.
  • Add a change file for the react-native-windows package.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp Fixes placeholder measurement sizing at non-100% scale (px→DIP) and repairs the default fontSize assignment when props are NaN.
change/react-native-windows-fix-textinput-placeholder-main.json Records the patch-level change for release notes/versioning.

@acoates-ms

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Contributor
Azure Pipelines:
Successfully started running 1 pipeline(s).

Change type from 'patch' to 'prerelease' for react-native-windows.
@acoates-ms

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Contributor
Azure Pipelines:
Successfully started running 1 pipeline(s).

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Performance Test Results

Branch: fix/textinput-placeholder-layout-main
Commit: 6bb9c8f8
Time: 2026-08-03T23:51:29.797Z
Tests: 160/161 passed

❌ Regressions Detected

SectionList

Scenario Baseline Current Change Status
SectionList rerender 11.60ms 14.80ms +47.6%

SectionList rerender: Duration increased by 47.6% / +5.00ms (threshold: 10% & 3ms)

✅ Passed

147 scenario(s) across 27 suite(s) — no regressions

FlatList

Scenario Mean Median StdDev Renders vs Baseline
FlatList mount 5.20ms 5.00ms ±1.48ms 1 +25.0%
FlatList unmount 0.10ms 0.00ms ±0.32ms 0 +0.0%
FlatList rerender 11.50ms 11.00ms ±2.84ms 2 +22.2%
FlatList with-10-items 5.70ms 5.00ms ±1.57ms 1 +25.0%
FlatList with-100-items 5.40ms 5.00ms ±0.70ms 1 +0.0%
FlatList with-500-items 4.70ms 5.00ms ±0.48ms 1 +25.0%
FlatList with-1000-items 4.40ms 4.50ms ±0.70ms 1 +12.5%
FlatList horizontal 5.30ms 5.00ms ±2.16ms 1 +0.0%
FlatList with-separator 2.10ms 2.00ms ±0.32ms 1 +0.0%
FlatList with-header-footer 1.70ms 2.00ms ±0.48ms 1 +0.0%
FlatList with-empty-list 0.50ms 0.50ms ±0.53ms 1 +0.0%
FlatList with-get-item-layout 2.00ms 2.00ms ±0.67ms 1 +100.0%
FlatList inverted 2.00ms 2.00ms ±0.94ms 1 +33.3%
FlatList with-num-columns 4.50ms 5.00ms ±0.71ms 1 +66.7%

TouchableOpacity

Scenario Mean Median StdDev Renders vs Baseline
TouchableOpacity mount 1.00ms 1.00ms ±0.00ms 1 +0.0%
TouchableOpacity unmount 0.10ms 0.00ms ±0.32ms 0 +0.0%
TouchableOpacity rerender 1.20ms 1.00ms ±0.63ms 2 +0.0%
TouchableOpacity custom-active-opacity 0.70ms 1.00ms ±0.48ms 1 +0.0%
TouchableOpacity disabled 0.80ms 1.00ms ±0.42ms 1 +0.0%
TouchableOpacity with-all-handlers 0.50ms 0.50ms ±0.53ms 1 -50.0%
TouchableOpacity with-hit-slop 0.90ms 1.00ms ±0.32ms 1 +0.0%
TouchableOpacity with-delay 0.60ms 1.00ms ±0.52ms 1 +0.0%
TouchableOpacity nested 1.60ms 2.00ms ±0.52ms 1 +100.0%
TouchableOpacity multiple-10 6.60ms 6.00ms ±1.64ms 1 +0.0%
TouchableOpacity multiple-50 28.60ms 28.00ms ±3.62ms 1 -3.4%
TouchableOpacity multiple-100 40.93ms 45.00ms ±11.17ms 1 -10.0%

ScrollView

Scenario Mean Median StdDev Renders vs Baseline
ScrollView mount 0.40ms 0.00ms ±0.52ms 1 +0.0%
ScrollView unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
ScrollView rerender 0.60ms 1.00ms ±0.52ms 2 +0.0%
ScrollView children-20 3.53ms 3.00ms ±1.30ms 1 -25.0%
ScrollView children-100 18.27ms 18.00ms ±3.37ms 1 +12.5%
ScrollView horizontal 3.70ms 4.00ms ±0.48ms 1 +0.0%
ScrollView sticky-headers 3.00ms 3.00ms ±1.15ms 1 +0.0%
ScrollView scroll-indicators 0.90ms 1.00ms ±0.32ms 1 +0.0%
ScrollView nested 1.50ms 1.50ms ±0.53ms 1 +50.0%
ScrollView content-container-style 0.80ms 1.00ms ±0.42ms 1 +0.0%
ScrollView children-500 21.53ms 21.00ms ±3.72ms 1 +10.5%

TouchableHighlight

Scenario Mean Median StdDev Renders vs Baseline
TouchableHighlight mount 0.40ms 0.00ms ±0.52ms 1 -100.0%
TouchableHighlight unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
TouchableHighlight rerender 0.70ms 1.00ms ±0.48ms 2 +0.0%
TouchableHighlight custom-underlay-color 0.60ms 1.00ms ±0.52ms 1 +Infinity%
TouchableHighlight custom-active-opacity 0.40ms 0.00ms ±0.52ms 1 +0.0%
TouchableHighlight disabled 0.30ms 0.00ms ±0.48ms 1 +0.0%
TouchableHighlight with-all-handlers 0.50ms 0.50ms ±0.53ms 1 +Infinity%
TouchableHighlight with-hit-slop 1.00ms 0.00ms ±2.16ms 1 +0.0%
TouchableHighlight nested-touchables 1.00ms 1.00ms ±0.00ms 1 +0.0%
TouchableHighlight multiple-touchables-10 2.50ms 3.00ms ±0.85ms 1 +0.0%
TouchableHighlight multiple-touchables-50 14.70ms 14.00ms ±2.71ms 1 +12.0%
TouchableHighlight multiple-touchables-100 26.50ms 26.00ms ±2.80ms 1 +15.6%

Pressable

Scenario Mean Median StdDev Renders vs Baseline
Pressable mount 0.40ms 0.00ms ±0.52ms 1 +0.0%
Pressable unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Pressable rerender 0.70ms 1.00ms ±0.48ms 2 +100.0%
Pressable with-all-handlers 0.50ms 0.50ms ±0.53ms 1 +Infinity%
Pressable with-style-function 0.40ms 0.00ms ±0.52ms 1 +0.0%
Pressable disabled 0.30ms 0.00ms ±0.48ms 1 +0.0%
Pressable with-hit-slop 0.40ms 0.00ms ±0.52ms 1 +0.0%
Pressable nested 0.80ms 1.00ms ±0.42ms 1 +0.0%
Pressable multiple-10 3.27ms 3.00ms ±0.46ms 1 +0.0%
Pressable multiple-50 16.80ms 16.00ms ±2.18ms 1 +14.3%
Pressable multiple-100 17.93ms 12.00ms ±10.72ms 1 +0.0%

Modal

Scenario Mean Median StdDev Renders vs Baseline
Modal mount 0.90ms 0.00ms ±1.85ms 1 +0.0%
Modal unmount 0.10ms 0.00ms ±0.32ms 0 +0.0%
Modal rerender 0.40ms 0.00ms ±0.52ms 2 +0.0%
Modal slide-animation 0.40ms 0.00ms ±0.52ms 1 +0.0%
Modal fade-animation 0.20ms 0.00ms ±0.42ms 1 +0.0%
Modal transparent 0.30ms 0.00ms ±0.48ms 1 +0.0%
Modal with-callbacks 0.30ms 0.00ms ±0.48ms 1 +0.0%
Modal rich-content 1.60ms 2.00ms ±0.52ms 1 +0.0%
Modal with-accessibility 0.40ms 0.00ms ±0.52ms 1 +0.0%

Image

Scenario Mean Median StdDev Renders vs Baseline
Image mount 0.00ms 0.00ms ±0.00ms 1 +0.0%
Image unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Image rerender 0.20ms 0.00ms ±0.42ms 2 +0.0%
Image with-resize-mode 0.30ms 0.00ms ±0.48ms 1 +0.0%
Image with-border-radius 0.20ms 0.00ms ±0.42ms 1 +0.0%
Image with-tint-color 0.10ms 0.00ms ±0.32ms 1 +0.0%
Image with-blur-radius 0.20ms 0.00ms ±0.42ms 1 +0.0%
Image with-accessibility 0.10ms 0.00ms ±0.32ms 1 +0.0%
Image multiple-10 1.00ms 1.00ms ±0.00ms 1 +0.0%
Image multiple-50 4.67ms 4.00ms ±1.45ms 1 +33.3%
Image multiple-100 10.00ms 10.00ms ±1.25ms 1 +25.0%

ActivityIndicator

Scenario Mean Median StdDev Renders vs Baseline
ActivityIndicator mount 0.20ms 0.00ms ±0.42ms 1 +0.0%
ActivityIndicator unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
ActivityIndicator rerender 0.30ms 0.00ms ±0.48ms 2 +0.0%
ActivityIndicator size-large 0.20ms 0.00ms ±0.42ms 1 +0.0%
ActivityIndicator size-small 0.20ms 0.00ms ±0.42ms 1 +0.0%
ActivityIndicator with-color 0.20ms 0.00ms ±0.42ms 1 +0.0%
ActivityIndicator not-animating 0.10ms 0.00ms ±0.32ms 1 +0.0%
ActivityIndicator with-accessibility 0.10ms 0.00ms ±0.32ms 1 +0.0%
ActivityIndicator multiple-10 0.87ms 1.00ms ±0.35ms 1 +0.0%
ActivityIndicator multiple-50 3.73ms 4.00ms ±0.59ms 1 +0.0%
ActivityIndicator multiple-100 9.07ms 8.00ms ±2.49ms 1 +14.3%

Switch

Scenario Mean Median StdDev Renders vs Baseline
Switch mount 0.10ms 0.00ms ±0.32ms 1 +0.0%
Switch unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Switch rerender 0.20ms 0.00ms ±0.42ms 2 -100.0%
Switch value-true 0.20ms 0.00ms ±0.42ms 1 +0.0%
Switch disabled 0.30ms 0.00ms ±0.48ms 1 +0.0%
Switch custom-colors 0.40ms 0.00ms ±0.52ms 1 +0.0%
Switch on-value-change 0.30ms 0.00ms ±0.48ms 1 +0.0%
Switch with-accessibility 0.20ms 0.00ms ±0.42ms 1 +0.0%
Switch multiple-10 2.20ms 2.00ms ±1.66ms 1 +0.0%
Switch multiple-50 11.13ms 9.00ms ±4.12ms 1 +0.0%
Switch multiple-100 21.00ms 19.00ms ±4.63ms 1 +18.8%

Button

Scenario Mean Median StdDev Renders vs Baseline
Button mount 0.70ms 1.00ms ±0.48ms 1 +0.0%
Button unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Button rerender 0.90ms 1.00ms ±0.57ms 2 +0.0%
Button disabled 0.60ms 1.00ms ±0.52ms 1 +0.0%
Button with-color 0.50ms 0.50ms ±0.53ms 1 +0.0%
Button with-accessibility 0.70ms 1.00ms ±0.48ms 1 +0.0%
Button multiple-10 5.93ms 6.00ms ±1.53ms 1 +0.0%
Button multiple-50 22.67ms 25.00ms ±8.80ms 1 -7.4%
Button multiple-100 16.60ms 17.00ms ±2.44ms 1 -10.5%

TextInput

Scenario Mean Median StdDev Renders vs Baseline
TextInput mount 0.20ms 0.00ms ±0.42ms 1 +0.0%
TextInput unmount 0.10ms 0.00ms ±0.32ms 0 +0.0%
TextInput rerender 0.20ms 0.00ms ±0.42ms 2 +0.0%
TextInput multiline 0.20ms 0.00ms ±0.42ms 1 +0.0%
TextInput with-value 0.20ms 0.00ms ±0.42ms 1 +0.0%
TextInput styled 0.10ms 0.00ms ±0.32ms 1 +0.0%
TextInput multiple-100 7.93ms 8.00ms ±1.10ms 1 +14.3%

View

Scenario Mean Median StdDev Renders vs Baseline
View mount 0.10ms 0.00ms ±0.32ms 1 +0.0%
View unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
View rerender 0.20ms 0.00ms ±0.42ms 2 +0.0%
View nested-50 4.20ms 4.00ms ±1.37ms 1 +33.3%
View nested-100 8.80ms 8.00ms ±2.04ms 1 +14.3%
View shadow 0.20ms 0.00ms ±0.42ms 1 +0.0%
View border-radius 0.20ms 0.00ms ±0.42ms 1 +0.0%
View nested-500 17.87ms 10.00ms ±13.51ms 1 +0.0%

Text

Scenario Mean Median StdDev Renders vs Baseline
Text mount 0.20ms 0.00ms ±0.42ms 1 +0.0%
Text unmount 0.00ms 0.00ms ±0.00ms 0 +0.0%
Text rerender 0.20ms 0.00ms ±0.42ms 2 +0.0%
Text long-1000 0.20ms 0.00ms ±0.42ms 1 +0.0%
Text nested 0.30ms 0.00ms ±0.48ms 1 +0.0%
Text styled 0.00ms 0.00ms ±0.00ms 1 +0.0%
Text multiple-100 9.27ms 9.00ms ±1.87ms 1 +28.6%

SectionList.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
SectionList native mount 4.84ms 4.42ms ±0.77ms 1 -32.1%

FlatList.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
FlatList native mount 4.84ms 4.59ms ±0.62ms 1 -50.3%

TouchableHighlight.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
TouchableHighlight native mount 1.45ms 1.42ms ±0.21ms 1 -31.9%

TouchableOpacity.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
TouchableOpacity native mount 1.65ms 1.58ms ±0.19ms 1 -49.5%

Pressable.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Pressable native mount 1.45ms 1.41ms ±0.17ms 1 -43.7%

ScrollView.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
ScrollView native mount 3.36ms 3.16ms ±0.44ms 1 -21.9%

ActivityIndicator.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
ActivityIndicator native mount 1.31ms 1.26ms ±0.22ms 1 -49.2%

TextInput.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
TextInput native mount 2.21ms 1.83ms ±0.86ms 1 -55.2%

Switch.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Switch native mount 1.11ms 1.08ms ±0.11ms 1 -37.7%

Button.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Button native mount 1.68ms 1.61ms ±0.23ms 1 -38.1%

Modal.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Modal native mount 0.90ms 0.86ms ±0.11ms 1 -29.3%

Image.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Image native mount 1.91ms 1.70ms ±0.56ms 1 -24.8%

View.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
View native mount 1.00ms 0.95ms ±0.15ms 1 -33.2%

Text.native-perf-test.ts

Scenario Mean Median StdDev Renders vs Baseline
Text native mount 1.30ms 1.33ms ±0.14ms 1 -23.4%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants