Description
On iOS with the New Architecture (Fabric), a <Text> without numberOfLines will sometimes clip the last word of a paragraph off the right edge instead of wrapping it to a new line. The dropped word is not truncated with an ellipsis — it is drawn past the container's right edge and simply disappears.
It is deterministic per (content × width × font size): it happens only when a paragraph's natural word-wrap would leave a single short "orphan" token on the final line (e.g. a paragraph ending in "…the platform that is coming." where only coming. would land on the last line). Change the font size or the container width so the last line holds two-plus words, and it wraps correctly.
Root cause
RCTTextLayoutManager.mm → -_textStorageAndLayoutManagerWithAttributesString:paragraphAttributes:size: sets:
textContainer.lineBreakMode = paragraphAttributes.maximumNumberOfLines > 0
? RCTNSLineBreakModeFromEllipsizeMode(paragraphAttributes.ellipsizeMode)
: NSLineBreakByClipping; // <-- used when numberOfLines is not set
With NSLineBreakByClipping, TextKit does not break the final line onto a new line when the remainder is a short orphan token — it keeps it on the previous line and clips it at the container's right edge. Because both the measurement pass (_measureTextStorage:) and the draw pass (drawAttributedString:) build the container the same way, onTextLayout even reports the crammed final line at exactly the container width (e.g. width: 396.0) — that clamped width is the observable signature of the merged/clipped line.
Switching that single line to NSLineBreakByWordWrapping (the mode already used implicitly on the happy path) fixes it with no other visible change. This is what apps that set numberOfLines={0}/omit it expect: normal word wrapping.
The offending line is still present on v0.86.0 and main (as of filing).
Steps to reproduce
- New Architecture enabled, iOS.
- Render a
<Text> with a fixed width and no numberOfLines, containing a multi-line paragraph whose natural wrap leaves a single short word alone on the final line.
- Observe the last word is clipped off the right edge (not wrapped, not ellipsized).
- Nudge the font size or width so the last line would hold ≥2 words → wraps correctly.
import { ScrollView, Text } from 'react-native';
// Width + copy chosen so the final line is a lone short token ("coming.").
export default function App() {
return (
<ScrollView contentContainerStyle={{ padding: 22 }}>
<Text style={{ width: 396, fontSize: 18, lineHeight: 30 }}>
They are the chapters that build your character, deepen your faith, and
prepare you for the platform that is coming.
</Text>
</ScrollView>
);
}
coming. is clipped off the right edge instead of wrapping to its own line. (Exact clipping depends on font metrics + width; the invariant is "final line = one short orphan token." A custom/condensed body font makes it easy to hit; it is not font-specific in principle since the cause is the container's lineBreakMode.)
React Native Version
0.85.3 (confirmed the offending line is unchanged on 0.86.0 and main)
Affected Platforms
Runtime - iOS (New Architecture / Fabric)
Output of npx @react-native-community/cli info
SDKs:
iOS SDK:
Platforms: [ iOS 26.2, ... ]
Managers:
CocoaPods: 1.16.2
IDEs:
Xcode: 26.2/17C52
Languages:
Java: 25.0.1
Ruby: 2.6.10
npmPackages:
react: 19.2.3
react-native: 0.85.3
Host: macOS (Darwin 25.5.0), Node 22.21.1, Expo SDK 56 (newArchEnabled: true).
Stacktrace or Logs
No crash/log — purely a visual layout defect. onTextLayout reports the merged final line at exactly the container width (width === containerWidth), which distinguishes it from normal wrapping.
Reproducer
Minimal snippet above (single <Text>, fixed width, no numberOfLines, orphan-word final line). The fix is a one-line change in RCTTextLayoutManager.mm:
textContainer.lineBreakMode = paragraphAttributes.maximumNumberOfLines > 0
? RCTNSLineBreakModeFromEllipsizeMode(paragraphAttributes.ellipsizeMode)
- : NSLineBreakByClipping;
+ : NSLineBreakByWordWrapping;
I can open a PR with this change if it's a welcome fix.
Screenshots and Videos
Before (clipped) vs after (patched to NSLineBreakByWordWrapping) — happy to attach if useful.
Description
On iOS with the New Architecture (Fabric), a
<Text>withoutnumberOfLineswill sometimes clip the last word of a paragraph off the right edge instead of wrapping it to a new line. The dropped word is not truncated with an ellipsis — it is drawn past the container's right edge and simply disappears.It is deterministic per (content × width × font size): it happens only when a paragraph's natural word-wrap would leave a single short "orphan" token on the final line (e.g. a paragraph ending in "…the platform that is
coming." where onlycoming.would land on the last line). Change the font size or the container width so the last line holds two-plus words, and it wraps correctly.Root cause
RCTTextLayoutManager.mm→-_textStorageAndLayoutManagerWithAttributesString:paragraphAttributes:size:sets:With
NSLineBreakByClipping, TextKit does not break the final line onto a new line when the remainder is a short orphan token — it keeps it on the previous line and clips it at the container's right edge. Because both the measurement pass (_measureTextStorage:) and the draw pass (drawAttributedString:) build the container the same way,onTextLayouteven reports the crammed final line at exactly the container width (e.g.width: 396.0) — that clamped width is the observable signature of the merged/clipped line.Switching that single line to
NSLineBreakByWordWrapping(the mode already used implicitly on the happy path) fixes it with no other visible change. This is what apps that setnumberOfLines={0}/omit it expect: normal word wrapping.The offending line is still present on v0.86.0 and
main(as of filing).Steps to reproduce
<Text>with a fixed width and nonumberOfLines, containing a multi-line paragraph whose natural wrap leaves a single short word alone on the final line.coming.is clipped off the right edge instead of wrapping to its own line. (Exact clipping depends on font metrics + width; the invariant is "final line = one short orphan token." A custom/condensed body font makes it easy to hit; it is not font-specific in principle since the cause is the container'slineBreakMode.)React Native Version
0.85.3 (confirmed the offending line is unchanged on 0.86.0 and
main)Affected Platforms
Runtime - iOS (New Architecture / Fabric)
Output of
npx @react-native-community/cli infoHost: macOS (Darwin 25.5.0), Node 22.21.1, Expo SDK 56 (
newArchEnabled: true).Stacktrace or Logs
No crash/log — purely a visual layout defect.
onTextLayoutreports the merged final line at exactly the container width (width === containerWidth), which distinguishes it from normal wrapping.Reproducer
Minimal snippet above (single
<Text>, fixed width, nonumberOfLines, orphan-word final line). The fix is a one-line change inRCTTextLayoutManager.mm:textContainer.lineBreakMode = paragraphAttributes.maximumNumberOfLines > 0 ? RCTNSLineBreakModeFromEllipsizeMode(paragraphAttributes.ellipsizeMode) - : NSLineBreakByClipping; + : NSLineBreakByWordWrapping;I can open a PR with this change if it's a welcome fix.
Screenshots and Videos
Before (clipped) vs after (patched to
NSLineBreakByWordWrapping) — happy to attach if useful.