Skip to content

Add lineBreak and wordBreak props to Text on Android - #59002

Open
iray-tno wants to merge 3 commits into
react:mainfrom
iray-tno:claude/react-native-changes-8rvmxi
Open

iray-tno wants to merge 3 commits into
react:mainfrom
iray-tno:claude/react-native-changes-8rvmxi

Conversation

@iray-tno

@iray-tno iray-tno commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

Android 13 (API 33) added LineBreakConfig, which controls two things CJK text needs and React Native cannot reach today:

  • Line-break strictness — CSS line-break. Strict kinsoku keeps characters such as small kana (ッ, ャ) and ー off the start of a line.
  • Phrase-based breaking — CSS word-break: auto-phrase. Japanese breaks at phrase (文節) boundaries instead of between any two characters, which reads much better in short lines such as buttons and headings.

This cannot be done from a library. React Native builds the StaticLayout that both measures text for Yoga and is drawn, inside TextLayoutManager. A native module can only change the drawn TextView, which then disagrees with the measured height (clipped or extra lines). So the setting has to go through React Native's own text pipeline.

This PR adds two Text props named after the CSS properties, documented as Android-only for now:

lineBreak?: 'auto' | 'loose' | 'normal' | 'strict'   // CSS line-break
wordBreak?: 'normal' | 'auto-phrase'                  // CSS word-break

CSS values with no Android equivalent (anywhere, keep-all, break-all) are left out for now.

  • JS / types: TextProps, TextNativeComponent view config, Text.d.ts, regenerated ReactNativeApi.d.ts.
  • C++: LineBreak / WordBreak enums, conversions, ParagraphAttributes fields (equality, hash, debug props), BaseParagraphProps parsing, Android props diffing, and two new paragraph MapBuffer keys. C++ API snapshots regenerated.
  • Android, measurement: TextLayoutManager applies the config to the StaticLayout.Builder on API 33+.
  • Android, drawing: ReactTextViewManager @ReactProp setters apply the same values to ReactTextView, so drawing matches measurement; recycled views reset them.

Behavior is unchanged unless the props are set. With the defaults ('auto' / 'normal') no LineBreakConfig is set at all, and below API 33 the props are ignored. iOS has no equivalent API, so this is Android-only for now. TextInput is intentionally left out to keep the change reviewable.

'auto-phrase' depends on locale. Per the Android docs, it has no effect in a locale without phrase-based breaking. Android Text is laid out in the device locale, so in practice it takes effect only when that locale supports it (e.g. Japanese). The prop docs say so.

Naming follows review feedback: CSS names and values rather than lineBreakStrategyAndroid, since lineBreakStrategyIOS and textBreakStrategy control different things.

Motivation and design notes: iray-tno/hozo#813

Changelog:

[ANDROID] [ADDED] - Add lineBreak and wordBreak props to Text (CSS line-break / word-break) for strict/loose line breaking and phrase-based breaking on Android 13+

Test Plan:

Ran locally:

  • yarn flow-check — 0 errors
  • yarn build-types then yarn test-generated-typescript — passes
  • yarn test — 246 suites, 6154 tests passed
  • yarn format-check-{javascript,cpp,kotlin,java} — clean
  • ESLint on the changed JS files — clean
  • yarn cxx-api-validate (Doxygen 1.16.1, as in CI) — all 12 snapshots match

Added:

  • C++ unit tests in ParagraphAttributesTest.cpp (equality/hash include the new fields, defaults are Auto / Normal, toString values match the JS prop values).
  • Updated the existing Robolectric tests that call TextLayoutManager internals for the new parameters. They pass the default values only, so they cover the plumbing, not the effect of strict / auto-phrase.
  • RNTester: Text → Line break (lineBreak, wordBreak) shows the same Japanese paragraph under each value at a fixed width, with a background so any measurement/drawing mismatch is visible.

Not run locally: native compilation (Kotlin/Java/C++), the C++ and Robolectric unit tests, and an on-device check. The environment had no Android SDK and Gradle could not resolve its plugins. Relying on CI for the native build and tests.

Still to verify on device, with the RNTester example:

  • API 33+, Japanese device locale: lineBreak (strict / loose) and wordBreak="auto-phrase" change the break positions
  • Measured height matches the drawn lines for each value (no clipped or blank lines)
  • Removing the prop returns to the default layout
  • A recycled Text does not keep a previous value
  • Non-Japanese device locale: auto-phrase has no effect, and nothing breaks
  • API 31/32: the props are ignored

🤖 Generated with Claude Code

@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 Oct 12, 2026
@github-actions

Copy link
Copy Markdown

Warning

JavaScript API change detected

This PR commits an update to ReactNativeApi.d.ts, indicating a change to React Native's public JavaScript API.

  • Please include a clear changelog message.
  • This change will be subject to additional review.

This change was flagged as: POTENTIALLY_BREAKING

@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 Oct 12, 2026
@javache

javache commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

/review

@github-actions

Copy link
Copy Markdown

🤖 AI code review

Decision: Ready for human review

Overall PR risk: Medium. The change modifies existing behavior in the Android Text measurement and rendering path, with blast radius limited to Android Text and direct revert. No critical or warning findings remain. The most plausible break is measured height and drawn lines differing if view state and layout config differ.

🔗 Review setup:

  • This PR changes code the reviewer prompts cite (packages/react-native/ReactAndroid/api/ReactAndroid.api). Check that the guidance quoting it is still correct.

No findings.


This review is advisory — it never blocks a merge and never auto-approves.

@meta-codesync

meta-codesync Bot commented Oct 13, 2026

Copy link
Copy Markdown

@javache has imported this pull request. If you are a Meta employee, you can view this in D124966798.

@javache

javache commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Can we align naming here with iOS and call this lineBreakStrategyAndroid (we're not very consistent here but suffix is more common...)

@javache

javache commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Even better, align the style names and values with CSS wherever possible and document as android-only for now.

iray-tno and others added 3 commits October 15, 2026 07:13
Android 13 (API 33) added LineBreakConfig, which selects the
strictness of the line-breaking rules (CSS `line-break`: loose,
normal, strict) and phrase-based breaking for Japanese (CSS
`word-break: auto-phrase`). React Native measures and draws Text with
a StaticLayout it builds itself, so neither setting can be reached from
outside: a native module could only change the drawn TextView, which
would then disagree with the height Yoga measured.

Add two Android-only paragraph props, plumbed the same way as
android_hyphenationFrequency:

- android_lineBreakStyle: 'none' | 'loose' | 'normal' | 'strict'
- android_lineBreakWordStyle: 'none' | 'phrase'

They go into ParagraphAttributes and the paragraph MapBuffer, are
applied to the StaticLayout.Builder in TextLayoutManager (measurement
and the prepared layout), and to ReactTextView through @ReactProp
setters (drawing). Both are ignored below API 33. When both are
'none' (the default) no LineBreakConfig is set, so existing text lays
out exactly as before.

TextInput is left out: it does not list the props in its view config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Regenerate the Frameworks C++ API snapshots (Common, Android, Apple)
for the new LineBreakStyle and LineBreakWordStyle enums, their
conversions, the ParagraphAttributes fields and the paragraph
MapBuffer keys. Generated with `yarn cxx-api-build` using Doxygen
1.16.1, the version the validation workflow pins.

Document that android_lineBreakWordStyle="phrase" depends on the
locale: Android Text is laid out in the device locale, and where that
locale has no phrase-based breaking the setting has no effect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per review, align the props with CSS `line-break` and `word-break`
instead of Android's LineBreakConfig terms, and document them as
Android-only for now:

- android_lineBreakStyle ('none' | 'loose' | 'normal' | 'strict')
  -> lineBreak ('auto' | 'loose' | 'normal' | 'strict')
- android_lineBreakWordStyle ('none' | 'phrase')
  -> wordBreak ('normal' | 'auto-phrase')

The C++ enums, ParagraphAttributes fields and MapBuffer keys follow
the CSS names. 'auto' and 'normal' still set no LineBreakConfig, so
default layout is unchanged. Regenerate the TypeScript and C++ API
snapshots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iray-tno
iray-tno force-pushed the claude/react-native-changes-8rvmxi branch from 0f0a658 to c4f4b9e Compare October 15, 2026 07:17
@iray-tno iray-tno changed the title Add android_lineBreakStyle and android_lineBreakWordStyle to Text Add lineBreak and wordBreak props to Text on Android Oct 15, 2026
@iray-tno

Copy link
Copy Markdown
Contributor Author

@javache Thanks for the review!

lineBreakStrategyAndroid could be confusing next to the existing lineBreakStrategyIOS and Android's textBreakStrategy, which control different things. So I went with your CSS suggestion and updated the PR:

  • android_lineBreakStyle -> lineBreak: 'auto' | 'loose' | 'normal' | 'strict' (CSS line-break)
  • android_lineBreakWordStyle -> wordBreak: 'normal' | 'auto-phrase' (CSS word-break)
  • Both documented as Android-only (API 33+) for now
  • CSS values with no Android equivalent (anywhere, keep-all, break-all) left out for now

I also rebased on main. Let me know if you'd prefer different names.

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.

2 participants