Skip to content

[Android] textDecorationLine underline disappears from an unchanged Text after a sibling is inserted (0.87.1) #58999

Open

Description

@ChoiHyeongu

Description

On Android, an underline set with textDecorationLine: 'underline' disappears from a <Text> whose own props and position did not change, once its parent commits a layout change around it (here: sibling rows inserted after it). The text still renders; only the decoration is gone. Newly mounted Text views, and Text views that moved, keep the underline. iOS is not affected.

It happens for both nested (<Text>a <Text style={{textDecorationLine: 'underline'}}>b</Text></Text>) and standalone underlined Text.

In 0.87 Android underline drawing moved from the framework UnderlineSpan to ReactUnderlineSpan (a CanvasEffectSpan painted from ReactTextView.onDraw → drawTextEffects → drawSpannedDecoration), as part of the textDecorationStyle work (87184c8). The symptom looks like that path draws nothing when an existing ReactTextView is only redrawn.

Possibly related, but different symptoms: #57900 (decoration position), #58356 (crash with numberOfLines).

We first saw it in an app where, among many identically styled rows, only some kept their underline — the ones that happened to be re-laid-out in the same commit.

Steps to reproduce

  1. Clone https://github.com/ChoiHyeongu/rn-087-android-underline-repro, then cd ReproducerApp && yarn install && yarn android.
  2. On launch, every row is underlined.
  3. Tap INSERT. Eight k rows are inserted between the n rows and the s rows.
  4. Every n row (unchanged, above the insertion) loses its underline. The inserted k rows and the shifted s rows keep it.

RERENDER (parent state change only) and REMOUNT (key change) do not trigger it.

React Native Version

0.87.1

Affected Platforms

Runtime - Android

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

System:
  OS: macOS 26.5
  CPU: (14) arm64 Apple M4 Pro
  Memory: 3.49 GB / 48.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 24.14.0
    path: ~/.local/share/mise/installs/node/24.14.0/bin/node
  Yarn:
    version: 1.22.22
    path: ~/.local/share/mise/installs/node/24.14.0/bin/yarn
  npm:
    version: 11.9.0
    path: ~/.local/share/mise/installs/node/24.14.0/bin/npm
  Watchman: Not Found
Managers:
  CocoaPods:
    version: 1.16.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 25.5
      - iOS 26.5
      - macOS 26.5
      - tvOS 26.5
      - visionOS 26.5
      - watchOS 26.5
  Android SDK: Not Found
IDEs:
  Android Studio: 2025.3 AI-253.32098.37.2534.15336583
  Xcode:
    version: 26.6/17F113
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 23.0.2
    path: /usr/bin/javac
  Ruby:
    version: 3.4.2
    path: ~/.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 20.2.0
    wanted: 20.2.0
  react:
    installed: 19.2.3
    wanted: 19.2.3
  react-native:
    installed: 0.87.1
    wanted: 0.87.1
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: Not found
  newArchEnabled: false

Stacktrace or Logs

No crash and no warnings — visual-only. The underline is simply not drawn.

MANDATORY Reproducer

https://github.com/ChoiHyeongu/rn-087-android-underline-repro

Screenshots and Videos

Pixel 9 Pro emulator, Android 16 (API 36).

Before After tapping INSERT

Activity

  1. Jakobbarnwell commented on Oct 20, 2026

    @Jakobbarnwell

    Confirmed the same visible failure on React Native 0.88.0 with a fresh, independent Expo 58.0.16 / React 19.3.0 app. Sibling insertion in a ScrollView can remove the decoration from unchanged text. The text itself remains visible.

    Public reproducer: https://github.com/Jakobbarnwell/react-native-android-underline-repro/tree/ecec8ad08014f78bd3584c4aa8856bbd4f0e9e33

    It has only Expo, React and React Native as dependencies, with an exact npm lockfile. No accounts, custom fonts, styling libraries or proprietary application code. The tested app source is e934a797ee95317de1bb99f564466e83994d0060; the linked revision adds the measurements and scripts without changing that app.

    Device/build: Android 11 / API 30, sdk_gphone_arm64 emulator, ARM64, 1080×2400, 420 dpi; release APK with embedded JS, Hermes and New Architecture enabled. No Metro or Expo Go. CLI/environment output, device details.

    Steps:

    1. Build/install the release APK using the repository README (npm ci, Expo prebuild, Gradle assembleRelease).
    2. Force-stop and launch com.jakobbarnwell.underlinerepro/.MainActivity; wait five seconds after am start -W returns.
    3. Observe A: nested default-font text, B: standalone control, C: nested wrapping semibold text.
    4. Tap INSERT SIBLINGS; eight sibling rows mount below those texts. Wait two seconds. Repeat from a new process because the failure is intermittent.

    Counted results (each of A, B and C had the same outcome):

    Condition Underline present Underline absent
    ScrollView, ten cold process launches before insertion 10/10 0/10
    Same trials, after insertion 6/10 4/10
    Fixed View control, cold launch 10/10 0/10
    Fixed View control, ten further launches after insertion 10/10 0/10

    ScrollView failures: runs 2, 3, 7, 9. Changing A's opacity restored it in 0/2 affected trials. In four separate fresh-process probes, two lost underlines and stayed missing after another ten seconds; both restored after a native debug.layout property/notification sequence (2/2 affected trials). The other two retained underlines throughout. The service command's Parcel(Error: ... "Not a data message") output is retained; this observation concerns recovery after the complete sequence, not a successful reply payload.

    ScrollView run 2: before and after insertion

    Full lossless PNGs, launch logs/PIDs, per-screenshot counts, hashes and probe instructions.

    Cold-launch-only loss was not reproduced. The possible null-getLayout() path in ReactTextView.onDraw remains an uninstrumented hypothesis. Counts are limited to this emulator; its host emitted Failed to find ColorBuffer warnings, and no physical-device or iOS validation was performed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions