Repository navigation
[iOS][Fabric] Text inside Modal keeps its old size after a live text size change (works on 0.86.3) #59375
Description
Description
On iOS with the New Architecture, Text inside a <Modal> doesn't follow a live system text size change. If the text size goes up while the app is open, every Text in the modal keeps the old size, while the TextInputs update. Relaunching fixes it. The same app on 0.86.3 updates correctly.
We hit this in our app through react-native-screens (expo-router native stack), whose screens take the same path, on the simulator and on an iPhone 16 Pro Max with iOS 26.7.1. There a later commit happens to repair the increase, but going from XXXL back to the default size leaves the screen large until it remounts.
What I found with logging in YogaLayoutableShadowNode on 0.88.0:
- On the font scale pass,
configureYogaTreedirties everyParagraphin the modal withmarkDirtyAndPropagate(YogaLayoutableShadowNode.cpp#L507-L512). - The propagation stops at
ModalHostView. Its descriptor callssetSizeinadopt(ModalHostViewComponentDescriptor.h#L31), andsetSizemarks the node withsetDirty(true)without propagating (YogaLayoutableShadowNode.cpp#L596-L606). Yoga'smarkDirtyAndPropagatereturns early on a node that's already dirty (Node.cpp#L453-L461). - So the modal's parent stays clean, Yoga reuses its cached layout,
ModalHostViewnever getshasNewLayout, andlayout()never writes the new metrics to the text nodes.
I haven't bisected 0.87, but #57246 moved the font scale handling into configureYogaTree, which is where this dirty mark starts. Related: #57512. I'm looking at a fix and will link a PR here.
Steps to reproduce
- Clone the reproducer, run
npm install, thencd ios && bundle install && bundle exec pod install. - Run a Release build on an iOS simulator at the default text size.
- With the app open, run
xcrun simctl ui <udid> content_size extra-extra-extra-large(or change Settings > Accessibility > Display & Text Size > Larger Text). - The text in the modal keeps its old size. Only the
TextInputs grow. - Relaunch the app at the same size. Now it's correct.
scripts/capture.sh <udid> runs steps 2 to 5 and saves the screenshots.
React Native Version
0.88.0
Affected Platforms
Runtime - iOS
Output of npx @react-native-community/cli info
System:
OS: macOS 26.7.1
CPU: (14) arm64 Apple M4 Pro
Memory: 2.79 GB / 48.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.21.1
path: /Users/simon/.nvm/versions/node/v22.21.1/bin/node
Yarn:
version: 1.22.22
path: /Users/simon/.nvm/versions/node/v22.21.1/bin/yarn
npm:
version: 10.9.4
path: /Users/simon/.nvm/versions/node/v22.21.1/bin/npm
Watchman:
version: 2026.05.25.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.16.2
path: /Users/simon/.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: Not Found
IDEs:
Android Studio: 2025.3 AI-253.32098.37.2534.15336583
Xcode:
version: 27.1/27A9275
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.19
path: /usr/bin/javac
Ruby:
version: 3.2.2
path: /Users/simon/.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, layout only. Trimmed excerpt from the logging I added to YogaLayoutableShadowNode.cpp, on the live change to XXXL. "stored" is layoutMetrics.fontSizeMultiplier, "chain" is the Paragraph's Yoga owners nearest first, and * marks a dirty Yoga node.
pass root=RootView#11 stored=1.000 new=1.353
dirty node=Paragraph#4 stored=1.000 new=1.353 chain=View View ModalHostView* View RootView*
nolayout parent=View#34 child=ModalHostView#32 childStored=1.000 new=1.353 staleTextNodes=7
MANDATORY Reproducer
https://github.com/simongt/rn-modal-text-size-repro
Screenshots and Videos
| Version | Default size | Live change to XXXL | Relaunch at XXXL |
|---|---|---|---|
| 0.88.0 | ![]() |
![]() |
![]() |
| 0.86.3 | ![]() |
![]() |
![]() |
iPhone 17 Pro simulator, iOS 26.5, Release builds with the default prebuilt core.






Activity
r1shabhsharma05 commentedon Oct 22, 2026
/assign
simongt commentedon Oct 23, 2026
Fix is up.
After @javache's review, it now works inside the pass that already pushes the new font scale down the tree: as that pass unwinds, a view marks itself dirty when a child it just updated came back dirty, the same rule layout already follows when a parent is rebuilt. Hidden views are skipped, and it's still behind the font scale flag.
@lazerg also opened this, with the same idea applied at the point where the child gets copied. Flagging it here so the two don't duplicate effort.
lazerg commentedon Oct 23, 2026
Closed mine
kabal1000x commentedon Oct 25, 2026
Same symptom without a
<Modal>, on 0.88.0 iOS (Fabric), in an Expo SDK 58 app (React 19.3.0), on iPhone SE (3rd gen) and iPhone 17 Pro Max simulators, iOS 26.3. The screen is a plain stack screen (Expo Router / react-native-screens 4.30.0) holding a fewTextand oneTextInputwith a placeholder. After switching Larger Text from the default to the largest accessibility size while the screen is mounted, everyTextkeeps its old size (checked at 1 s and 6 s), and theTextInputplaceholder draws at the new size inside its unchanged box. A freshly mounted screen at the same setting lays out correctly. Mounted text catches up only when something else commits a layout change.Reading the 0.88.0 sources, there is a gap before
configureYogaTreeis ever reached, which #59383 would not cover:RCTFabricSurfaceobservesUIContentSizeCategoryDidChangeNotificationand calls_updateLayoutContext, which setslayoutContext.fontSizeMultiplier = RCTFontSizeMultiplier()and calls_surfaceHandler->constraintLayout(layoutConstraints, layoutContext). The constraints are unchanged.SurfaceHandler::constraintLayoutcommitsoldRootShadowNode.clone(propsParserContext, layoutConstraints, layoutContext)and nothing else.RootShadowNode::clonedirties only on a constraints change:RootShadowNode::layoutIfNeededreturns early whengetIsLayoutClean(), solayoutTree→configureYogaTree(where theenableFontScaleChangesUpdatingLayoutcheck now lives since Fix Fabric reusing nodes with a stale font scale (#57246) #57246) never runs for this commit.Before #57246,
SurfaceHandler::constraintLayouton 0.86-stable calleddirtyMeasurableNodes(*newRoot)whenfontSizeMultiplierchanged. #57246 removed that call and the helpers.mainstill has the 0.88.0RootShadowNode::cloneandSurfaceHandler::constraintLayout.#57124 (open, labelled Android) adds exactly the missing step in shared code,
newRoot->dirtyLayout()whenfontSizeMultiplierchanges, so it looks relevant to iOS too, and possibly needed alongside #59383. The general report for this symptom is #45655.Screenshots (before / mounted after change / fresh mount) available; can supply a minimal repro on request.