Skip to content

Fix Fabric reusing nodes with a stale font scale (#57246) - #57246

Closed
j-piasecki wants to merge 1 commit into
react:mainfrom
j-piasecki:@jpiasecki/dirty-yoga-cache-on-font-scale-change
Closed

j-piasecki wants to merge 1 commit into
react:mainfrom
j-piasecki:@jpiasecki/dirty-yoga-cache-on-font-scale-change

Conversation

@j-piasecki

@j-piasecki j-piasecki commented Jun 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary:
Fixes #52895

The original solution to this issue approached this by comparing the font scale used to lay out respective root nodes and re-cloning measurable nodes in the entire tree when it was different. This didn't work when a node within the tree was cloned from a node with an obsolete font scale stored (with root having the correct one). The new node would use the obsolete value in that pass.

This PR changes this approach - instead of comparing font scale in SurfaceHandler::constraintLayout (only when it changes), it moves the comparison to happen as part of configureYogaTree, similarly to pointScaleFactor. This way, nodes with an obsolete value stored get re-cloned using the correct one on each commit instead of only on the first one.

Changelog:

[GENERAL][FIXED] - Fixed Fabric reusing nodes with a stale font scale

Test Plan:
Issue reproducer

Before After
Android
Screen.Recording.2026-06-10.at.11.18.28.mov
Screen.Recording.2026-06-10.at.11.15.46.mov
iOS
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-06-10.at.11.19.05.mov
Simulator.Screen.Recording.-.iPhone.17.Pro.-.2026-06-10.at.11.16.37.mov

Reviewed By: javache

Differential Revision: D108877387

Pulled By: j-piasecki

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 17, 2026
@facebook-github-tools facebook-github-tools Bot added p: Software Mansion Partner: Software Mansion Partner p: Facebook Partner: Facebook labels Jun 17, 2026
@meta-codesync

meta-codesync Bot commented Jun 17, 2026

Copy link
Copy Markdown

@j-piasecki has imported this pull request. If you are a Meta employee, you can view this in D108877387.

@meta-codesync meta-codesync Bot changed the title Fix Fabric reusing nodes with a stale font scale Fix Fabric reusing nodes with a stale font scale (#57246) Jun 17, 2026
@j-piasecki
j-piasecki force-pushed the @jpiasecki/dirty-yoga-cache-on-font-scale-change branch from 2454c01 to bcabdf3 Compare June 17, 2026 12:53
j-piasecki added a commit to j-piasecki/react-native that referenced this pull request Jun 17, 2026
Summary:
Fixes react#52895

The original solution to this issue approached this by comparing the font scale used to lay out respective root nodes and re-cloning measurable nodes in the entire tree when it was different. This didn't work when a node within the tree was cloned from a node with an obsolete font scale stored (with root having the correct one). The new node would use the obsolete value in that pass.

This PR changes this approach - instead of comparing font scale in `SurfaceHandler::constraintLayout` (only when it changes), it moves the comparison to happen as part of `configureYogaTree`, similarly to `pointScaleFactor`. This way, nodes with an obsolete value stored get re-cloned using the correct one on each commit instead of only on the first one.

## Changelog:

[GENERAL][FIXED] - Fixed Fabric reusing nodes with a stale font scale


Test Plan:
Issue reproducer

||Before|After|
|-|-|-|
|Android|<video src="https://github.com/user-attachments/assets/0b30ca95-76e9-4ce9-8d8f-418f9d827bea" />|<video src="https://github.com/user-attachments/assets/009be13c-c3b5-4af0-8be1-b44c94ac0496" />|
|iOS|<video src="https://github.com/user-attachments/assets/b861b622-a2ea-4cc4-a8db-0e3ad4cae5e7" />|<video src="https://github.com/user-attachments/assets/ec4d6f6e-7558-4430-813b-61a6e911c3b0" />|

Differential Revision: D108877387

Pulled By: j-piasecki
@meta-codesync

meta-codesync Bot commented Jun 17, 2026

Copy link
Copy Markdown

@j-piasecki has exported this pull request. If you are a Meta employee, you can view the originating Diff in D108877387.

Summary:
Fixes react#52895

The original solution to this issue approached this by comparing the font scale used to lay out respective root nodes and re-cloning measurable nodes in the entire tree when it was different. This didn't work when a node within the tree was cloned from a node with an obsolete font scale stored (with root having the correct one). The new node would use the obsolete value in that pass.

This PR changes this approach - instead of comparing font scale in `SurfaceHandler::constraintLayout` (only when it changes), it moves the comparison to happen as part of `configureYogaTree`, similarly to `pointScaleFactor`. This way, nodes with an obsolete value stored get re-cloned using the correct one on each commit instead of only on the first one.

## Changelog:

[GENERAL][FIXED] - Fixed Fabric reusing nodes with a stale font scale


Test Plan:
Issue reproducer

||Before|After|
|-|-|-|
|Android|<video src="https://github.com/user-attachments/assets/0b30ca95-76e9-4ce9-8d8f-418f9d827bea" />|<video src="https://github.com/user-attachments/assets/009be13c-c3b5-4af0-8be1-b44c94ac0496" />|
|iOS|<video src="https://github.com/user-attachments/assets/b861b622-a2ea-4cc4-a8db-0e3ad4cae5e7" />|<video src="https://github.com/user-attachments/assets/ec4d6f6e-7558-4430-813b-61a6e911c3b0" />|

Reviewed By: javache

Differential Revision: D108877387

Pulled By: j-piasecki
@j-piasecki
j-piasecki force-pushed the @jpiasecki/dirty-yoga-cache-on-font-scale-change branch from bcabdf3 to c4d84b1 Compare June 17, 2026 13:39
@meta-codesync meta-codesync Bot closed this in 45904c8 Jun 18, 2026
@meta-codesync meta-codesync Bot added the Merged This PR has been merged. label Jun 18, 2026
@meta-codesync

meta-codesync Bot commented Jun 18, 2026

Copy link
Copy Markdown

@j-piasecki merged this pull request in 45904c8.

curiosity26 added a commit to curiosity26/react-native-gtk4 that referenced this pull request Oct 11, 2026
…ive 0.83

0.83's SurfaceHandler dirties measurable nodes only in the commit where
the font scale changes; later commits bring back React's nodes laid out at
the old scale, and Yoga reuses their layout, so Text kept GNOME's 1.5x
size after text scaling went back to 1.0. Upstream fixed it after 0.83
(react/react-native#57246: layouts record their font scale in
LayoutMetrics::fontSizeMultiplier).

FontScaleCommitHook does the same from a UIManager commit hook: once a
surface's font scale has changed, each commit clones and dirties the
measurable nodes that are laid out but weren't committed at the current
scale (and the path to them). The host registers it on each instance's
UIManager only where LayoutMetrics has no fontSizeMultiplier, so it's
inert on 0.87. gallery-platform-self-test checks Text's height going back
on 0.83 too, and docs/upgrading.md lists the fix instead of the
limitation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
curiosity26 added a commit to curiosity26/react-native-gtk4 that referenced this pull request Oct 11, 2026
…ive 0.83

0.83's SurfaceHandler dirties measurable nodes only in the commit where
the font scale changes; later commits bring back React's nodes laid out at
the old scale, and Yoga reuses their layout, so Text kept GNOME's 1.5x
size after text scaling went back to 1.0. Upstream fixed it after 0.83
(react/react-native#57246: layouts record their font scale in
LayoutMetrics::fontSizeMultiplier).

FontScaleCommitHook does the same from a UIManager commit hook: once a
surface's font scale has changed, each commit clones and dirties the
measurable nodes that are laid out but weren't committed at the current
scale (and the path to them). The host registers it on each instance's
UIManager only where LayoutMetrics has no fontSizeMultiplier, so it's
inert on 0.87. gallery-platform-self-test checks Text's height going back
on 0.83 too, and docs/upgrading.md lists the fix instead of the
limitation.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 77050bc)
rhoadsjustin added a commit to rhoadsjustin/meno that referenced this pull request Oct 26, 2026
Changing the iOS text size while Meno was open left every label clipped
until the next launch: the glyphs grew, but their frames kept the height
measured at the old size.

Root cause is in React Native 0.86's Fabric renderer, not in Meno. On a
content-size change, SurfaceHandler re-measures text by cloning the text
nodes on the native side. React never learns about those clones, so its
next commit (a tab switch, any state update) puts back the nodes it holds.
Those carry the old measurement, while state reconciliation hands them the
new, larger attributed string. Measured on iPhone 17 (large to AX5, one tab
switch later): 58 of 58 text frames across Today, Library, Stats and
Settings were stale. Upstream fixed this in 0.87.0
(react/react-native#57246, issue #57512); the 0.86 backport is still an
open pick request.

The upstream fix is C++ and Meno uses the prebuilt React Native core, so
a native backport would mean building React Native from source on every
local and EAS build. Instead, a patch-package patch to Text.js counts
font-scale changes (from Dimensions) and keys each root text element on
the count. React then creates fresh native text nodes, measured at the
current scale, once per change. Only the native text nodes are rebuilt:
components and their state stay mounted, so a practice session carries on
mid-round. After the patch the same run matches a cold launch at AX5 for
all 58 text frames. TextInput and the SwiftUI hosts already re-measure on
their own and are untouched.

Drop the patch when Meno moves to React Native 0.87 or later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rhoadsjustin added a commit to rhoadsjustin/meno that referenced this pull request Oct 26, 2026
Changing the iOS text size while Meno was open left every label clipped
until the next launch: the glyphs grew, but their frames kept the height
measured at the old size.

Root cause is in React Native 0.86's Fabric renderer, not in Meno. On a
content-size change, SurfaceHandler re-measures text by cloning the text
nodes on the native side. React never learns about those clones, so its
next commit (a tab switch, any state update) puts back the nodes it holds.
Those carry the old measurement, while state reconciliation hands them the
new, larger attributed string. Measured on iPhone 17 (large to AX5, one tab
switch later): 58 of 58 text frames across Today, Library, Stats and
Settings were stale. Upstream fixed this in 0.87.0
(react/react-native#57246, issue #57512); the 0.86 backport is still an
open pick request.

The upstream fix is C++ and Meno uses the prebuilt React Native core, so
a native backport would mean building React Native from source on every
local and EAS build. Instead, a patch-package patch to Text.js counts
font-scale changes (from Dimensions) and keys each root text element on
the count. React then creates fresh native text nodes, measured at the
current scale, once per change. Only the native text nodes are rebuilt:
components and their state stay mounted, so a practice session carries on
mid-round. After the patch the same run matches a cold launch at AX5 for
all 58 text frames. TextInput and the SwiftUI hosts already re-measure on
their own and are untouched.

Drop the patch when Meno moves to React Native 0.87 or later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. meta-exported p: Facebook Partner: Facebook p: Software Mansion Partner: Software Mansion Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[New Arch][Android] The text gets cut off when the system font size/weight is increased

2 participants