Description
On iOS/Fabric in React Native 0.86.2, any <Text> style that combines a PostScript-named fontFamily with an explicit non-default fontWeight renders the heaviest face in the family instead of the requested weight.
// Renders Poppins-Medium (correct)
<Text style={{ fontFamily: 'Poppins-Medium' }} />
// Renders Poppins-Bold (bug — expected Poppins-Medium)
<Text style={{ fontFamily: 'Poppins-Medium', fontWeight: '500' }} />
// Renders Poppins-Bold (bug — expected Poppins-SemiBold)
<Text style={{ fontFamily: 'Poppins-SemiBold', fontWeight: '600' }} />
// Bare family name + weight resolves correctly (different code path)
<Text style={{ fontFamily: 'Poppins', fontWeight: '500' }} /> // correct
Verified via the rendered NSAttributedString font runs (not UIFont lookups): the fonts are registered and loadable; the resolver selects the wrong face.
Root cause
ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontUtils.mm (~line 364):
fontWeight = (fontWeight != 0.0) ?: RCTGetFontWeight(font);
The GNU ?: (elvis) operator assigns the boolean result of the comparison — 1.0 — for any explicit nonzero weight, rather than preserving the original fontWeight value. UIFontWeight 1.0 is the heaviest weight, so the subsequent family search selects the boldest face.
Suggested fix
fontWeight = (fontWeight != 0.0) ? fontWeight : RCTGetFontWeight(font);
(Note: this still cannot distinguish an explicit fontWeight: '400' from "no weight," since UIFontWeightRegular == 0.0 — a separate, pre-existing limitation.)
Impact
Any app pairing custom-font PostScript names with explicit weights (a common pattern, and the style many older codebases carry from pre-Fabric versions where the named face won) renders bold text across the board after upgrading. We hit this migrating a production app from 0.81.5 to 0.86.2 — every fontFamily: 'Poppins-<Face>' + matching fontWeight pair (~450 style sites) collapsed to Poppins-Bold. We are carrying a one-line patch-package fix of the ternary, which restores correct resolution for all pairings.
Steps to reproduce
- Bundle a multi-weight custom font family (e.g. Poppins Regular/Medium/SemiBold/Bold) via
UIAppFonts.
- Render
<Text style={{ fontFamily: 'Poppins-Medium', fontWeight: '500' }}>test</Text> on 0.86.2 with Fabric.
- Inspect the rendered attributed string: the resolved font is
Poppins-Bold.
Environment
React Native 0.86.2 (Fabric / new architecture), iOS 26.5 simulator + device, Expo SDK 57 prebuild (bare workflow equivalent). Regression vs 0.81.5 behavior.
Description
On iOS/Fabric in React Native 0.86.2, any
<Text>style that combines a PostScript-namedfontFamilywith an explicit non-defaultfontWeightrenders the heaviest face in the family instead of the requested weight.Verified via the rendered
NSAttributedStringfont runs (notUIFontlookups): the fonts are registered and loadable; the resolver selects the wrong face.Root cause
ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontUtils.mm(~line 364):fontWeight = (fontWeight != 0.0) ?: RCTGetFontWeight(font);The GNU
?:(elvis) operator assigns the boolean result of the comparison —1.0— for any explicit nonzero weight, rather than preserving the originalfontWeightvalue.UIFontWeight1.0 is the heaviest weight, so the subsequent family search selects the boldest face.Suggested fix
fontWeight = (fontWeight != 0.0) ? fontWeight : RCTGetFontWeight(font);(Note: this still cannot distinguish an explicit
fontWeight: '400'from "no weight," sinceUIFontWeightRegular == 0.0— a separate, pre-existing limitation.)Impact
Any app pairing custom-font PostScript names with explicit weights (a common pattern, and the style many older codebases carry from pre-Fabric versions where the named face won) renders bold text across the board after upgrading. We hit this migrating a production app from 0.81.5 to 0.86.2 — every
fontFamily: 'Poppins-<Face>'+ matchingfontWeightpair (~450 style sites) collapsed toPoppins-Bold. We are carrying a one-linepatch-packagefix of the ternary, which restores correct resolution for all pairings.Steps to reproduce
UIAppFonts.<Text style={{ fontFamily: 'Poppins-Medium', fontWeight: '500' }}>test</Text>on 0.86.2 with Fabric.Poppins-Bold.Environment
React Native 0.86.2 (Fabric / new architecture), iOS 26.5 simulator + device, Expo SDK 57 prebuild (bare workflow equivalent). Regression vs 0.81.5 behavior.