Skip to content

Text rendering cut off on Android 15 & 16 #56402

Description

@atlj

Description

PR #56282 reintroduced the issue #53286, please see it for more details. This brings that issue to version 0.85.0.

Quoting the original issue:

Text content is partially cut off, making some characters / words not fully visible. This impacts readability and layout consistency.

Steps to reproduce

Run the reproduction repo on Android 15 or 16 and observe text is cut off, while on older versions like Android 13/14 it displays correctly.

React Native Version

0.85.0

Affected Platforms

Runtime - Android

Output of npx @react-native-community/cli info

info Fetching system and libraries information...
System:
  OS: macOS 15.7.4
  CPU: (12) arm64 Apple M4 Pro
  Memory: 115.38 MB / 48.00 GB
  Shell:
    version: 0.109.1
    path: /Users/burakguner/.cargo/bin/nu
Binaries:
  Node:
    version: 22.22.0
    path: /Users/burakguner/.nix-profile/bin/node
  Yarn:
    version: 1.22.22
    path: /Users/burakguner/.nix-profile/bin/yarn
  npm:
    version: 10.9.4
    path: /Users/burakguner/.nix-profile/bin/npm
  Watchman:
    version: 2026.01.19.00
    path: /Users/burakguner/.nix-profile/bin/watchman
Managers:
  CocoaPods:
    version: 1.16.2
    path: /Users/burakguner/.local/share/gem/ruby/3.3.0/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 25.2
      - iOS 26.2
      - macOS 26.2
      - tvOS 26.2
      - visionOS 26.2
      - watchOS 26.2
  Android SDK:
    API Levels:
      - "30"
      - "31"
      - "34"
      - "35"
      - "36"
    Build Tools:
      - 35.0.0
      - 36.0.0
      - 36.1.0
    System Images:
      - android-30 | ARM 64 v8a
      - android-34 | Google Play ARM 64 v8a
      - android-35 | Google APIs ARM 64 v8a
      - android-35 | Google Play ARM 64 v8a
      - android-36.1 | Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2025.3 AI-253.31033.145.2533.15113396
  Xcode:
    version: 26.2/17C52
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 21.0.8
    path: /Users/burakguner/.nix-profile/bin/javac
  Ruby:
    version: 3.4.8
    path: /Users/burakguner/.nix-profile/bin/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 20.1.0
    wanted: 20.1.0
  react:
    installed: 19.2.3
    wanted: 19.2.3
  react-native:
    installed: 0.85.0
    wanted: 0.85.0
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: Not found
  newArchEnabled: false

Stacktrace or Logs

N/A

MANDATORY Reproducer

https://github.com/atlj/reproducer-android-text-rendering

Screenshots and Videos

Android 14 Android 15 Android 16
Image Image Image

Activity

  1. otang commented on Aug 2, 2026

    @otang

    The useBoundsForWidth fix (8347cc4 / #54721) has a Float-precision bug that still wraps right- and center-aligned text - worth fixing when this is re-landed.

    While backporting 8347cc4 to 0.83 we found that the fix works for left-aligned text, but right/center-aligned text still wraps its last glyph mid-word (e.g. a single-line label Weather renders as Weathe with r wrapped to a second line).

    Root cause: the unconstrained measuring pass builds a layout that is Int.MAX_VALUE / 2 wide using the caller's alignment, then computes line width as:

    val lineWidth = unconstrainedLayout.getLineRight(i) - unconstrainedLayout.getLineLeft(i)

    With ALIGN_OPPOSITE or ALIGN_CENTER, the line's x-coordinates sit near 2^30, where Float ULP is 64-128. getLineRight() and getLineLeft() therefore return values quantized to a ~64-128px grid, and their difference routinely under-reports the true line width by tens of pixels. The final layout is then built at that too-small width and the text wraps. Left-aligned text is unaffected because its coordinates are near zero, where Float is precise - which makes the bug easy to miss in testing.

    Repro: Android 15/16 (API 35/36, targetSdk >= 35), any auto-width <Text style={{ textAlign: 'right' }}> - observed on a Pixel 9 Pro API 36 emulator with a 7-character label at fontSize 24.

    Fix that worked for us: build the measuring pass with Layout.Alignment.ALIGN_NORMAL regardless of the caller's alignment - alignment never affects intrinsic line width, and it keeps coordinates in the precise Float range. (getLineMax(i) may be an even better primitive, since it computes the extent without alignment offsets and would also cover RTL paragraphs, which remain right-anchored even under ALIGN_NORMAL.)

    Secondary issue in the same commit: the Android 15+ branch's layoutWidth when dropped the YogaMeasureMode.EXACTLY case (only AT_MOST and else remain), so fixed-width text falls into else and gets its unconstrained visual width - which disables wrapping for EXACTLY-measured text. The PR summary says EXACTLY is unaffected, so this looks like an oversight. Restoring EXACTLY -> ceil(width).toInt() (ceil per the current main behavior, not floor, to avoid the subpixel last-glyph wrap) fixed it for us.

    Happy to provide screenshots or test on a re-land PR.

  2. bheemreddy-samsara commented on Aug 14, 2026

    @bheemreddy-samsara

    Additional data points from a chat-style message bubble UI (originally filed as #57957, closed as duplicate of this family):

    • Repro: react-native 0.80.3, New Architecture (Fabric), Hermes, physical Samsung Galaxy Tab S11 on Android 16. A <Text> that wraps to a second line inside a content-sized container is measured one line short and the last wrapped line is clipped. String-dependent: bodies whose wrap point lands close to the available width clip reliably; others render fine. iOS and older Android versions are unaffected, consistent with the useBoundsForWidth behavior change in Android 15+.
    • lineHeight: undefined did NOT fix it in our case (mentioned as a workaround in Text rendering cut off on Android 15 & 16 in both old and new architecture #53286): we A/B tested on the same device with no lineHeight set on the text and the last line still clipped.
    • Also ruled out on device: replacing percentage maxWidth ancestors with definite widths, and rendering the body as a single Text node instead of nested fragments.
    • Working workaround: subscribe to onTextLayout, then apply minHeight = lines.length * lineHeight to the text node and its wrapper (with a conservative fallback before the first layout event). This corrects the under-measured height without mutating text content, unlike the trailing-space hack.
  3. troZee commented on Aug 31, 2026

    @troZee
    Contributor

    I tested @atlj 's repro inside https://github.com/mdjastrzebski/react-native-plain-text, and it fixes the issue 🎉

    Image
  4. added a commit that references this issue on Sep 2, 2026
    b423735
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions