Skip to content

Honor the PlatformColor fallback in ColorPropConverter on Android - #59989

Open
jpudysz wants to merge 1 commit into
react:mainfrom
jpudysz:fix/android-platformcolor-fallback
Open

jpudysz wants to merge 1 commit into
react:mainfrom
jpudysz:fix/android-platformcolor-fallback

Conversation

@jpudysz

@jpudysz jpudysz commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Summary:

On Android, a PlatformColor whose name doesn't resolve crashes the app, even when a fallback is set. This PR makes Android paint the fallback, or transparent when there is none, the same as iOS and the Android C++ renderer already do.

The problem

RN 0.88 added a lazy raw-color fallback to PlatformColor (#57701, #57655, #57556):

<View style={{ backgroundColor: PlatformColor('?attr/notAnAttr', { fallback: '#ff0000' }) }} />
Platform Result
iOS ✅ Paints #ff0000
Android ❌ Crashes (red box in dev, crash in release)
Stack trace
com.facebook.react.bridge.JSApplicationIllegalArgumentException:
  Error while updating property 'backgroundColor' of a view managed by: RCTView
Caused by: com.facebook.react.bridge.JSApplicationCausedNativeException:
  ColorValue: None of the paths in the `resource_paths` array resolved to a color resource.
    at ViewManagersPropertyCache.updateViewProp
    at ViewManager.updateProperties
    at ViewManager.createViewInstance
    at SurfaceMountingManager.preallocateView

Root cause

  1. JS sends the color to native as {resource_paths: [...], fallback: '#ff0000'}.
  2. The C++ renderer honors fallback (PlatformColorParser.cpp).
  3. On Android, View props also reach the Java ViewManagers as raw props: enablePropsUpdateReconciliationAndroid, which would send the C++-parsed value instead, defaults to false
  4. Those setters convert colors with ColorPropConverter, which never reads fallback and throws when no path resolves.

Every Java color path goes through ColorPropConverter, so all of them are affected: @ReactProp color setters (including TextInput colors), BaseViewManagerDelegate, ripple, box shadow, gradients and animated colors.

The fix

When no resource path resolves, ColorPropConverter now:

  1. Uses the fallback, parsed by the same C++ CSS color parser that the C++ renderer and iOS use (parseCSSProperty<CSSColor> → hostPlatformColorFromRGBA). Hex, rgb(), rgba(), hsl(), hsla(), hwb() and named colors resolve exactly as on the other paths, with no Kotlin copy of the parser to drift.
  2. Otherwise paints transparent, when there is no fallback or it doesn't parse. The existing "Failed to resolve resource paths" warning still logs.

The parser is reached through a small JNI helper, RawColorParser, laid out like the existing ScrollEventHelper and TouchesHelper. It only runs on a miss that has a fallback; colors that resolve take the same path as before.

File Change
bridge/ColorPropConverter.kt Fallback, then transparent, instead of throwing
bridge/RawColorParser.kt JNI entry point: nativeParse(value): Int?
jni/react/jni/RawColorParser.{h,cpp} Shared C++ CSS color parser → Android color
jni/react/jni/OnLoad.cpp, CMakeLists.txt Register the native method, link react_renderer_css
scripts/cxx-api/api-snapshots/ReactAndroid*Cxx.api New JNI header in the C++ API snapshots
ColorPropConverterTest.kt, ShadowRawColorParser.kt Tests, with the native parser stubbed

Behavior across platforms

Unresolved PlatformColor iOS Android C++ Android Java before Android Java after
Valid fallback fallback fallback ❌ crash ✅ fallback
Invalid fallback transparent transparent ❌ crash ✅ transparent
No fallback transparent transparent ❌ crash ✅ transparent

Important

Behavior change on Android. An unresolved PlatformColor with no fallback used to throw through the @ReactProp color setters. It now paints transparent and logs a warning, matching iOS, the Android C++ renderer and the RN Tester example ("miss → transparent"). This is intentional.

Changelog:

[ANDROID] [FIXED] - Honor the PlatformColor fallback and paint unresolved colors transparent instead of crashing

Test Plan:

Automated

Check Command Result
Unit tests ./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest --tests 'com.facebook.react.bridge.ColorPropConverterTest' ✅ 8 tests, 0 failures
C++ API snapshots yarn cxx-api-validate (Doxygen 1.16.1, same as CI) ✅ 12 of 12 match
Formatting yarn format-check-kotlin, yarn format-check-cpp ✅ Clean

The 6 new tests cover a valid fallback, the fallback beating a caller's default value, a transparent fallback, a resolved path ignoring the fallback, and the missing and invalid fallback cases. Against the old ColorPropConverter, the fallback tests fail.

Manual: RN Tester

Pixel 8 Pro emulator, Android 14, RN Tester built from source:

  1. ./gradlew :packages:rn-tester:android:app:assembleDebug -PreactNativeArchitectures=arm64-v8a
  2. Open PlatformColor → Lazy Fallback Colors (rntester://example/PlatformColorExample).
Screendrop_2026-11-13-11-47-53

After the fix:

  • The no fallback row is transparent (outlined).
  • Hex, rgb(), rgba() and #RRGGBBAA fallbacks paint the expected colors on backgroundColor, borderColor and text color.
  • Changing one fallback to 'not-a-color' renders that row transparent, with no crash.

Not run

The full CI matrix and iOS. iOS code is unchanged by this PR.

ColorPropConverter threw when no resource path resolved, ignoring the raw
`fallback` string that `PlatformColor(..., {fallback})` sends. Java prop
setters (backgroundColor, borderColor, TextInput colors, ripple) crashed
while the C++ renderer painted the fallback. Parse the fallback with the
shared C++ CSS color parser over JNI, and paint transparent when nothing
resolves, matching the C++ renderer and iOS.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 13, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Nov 13, 2026

This branch has not been deployed

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant