Skip to content

[Android] EnrichedText adds large phantom trailing space when external HTML contains long attributes (e.g. a long href) #700

Description

@ShanavasPS

Version

react-native-enriched-html@1.0.0, Android, New Architecture.

Summary

When EnrichedText renders external HTML (a string not wrapped in <html>…</html>) with useHtmlNormalizer, the Android view is measured much taller than it renders. The extra height shows up as a large empty gap after the content. The gap grows with the amount of markup — a single anchor with a long href is enough to add ~15–20 phantom lines.

iOS renders the identical content with the correct height.

Root cause

The render path normalizes/parses the HTML, but the measure path does not.

EnrichedTextView.parseText (render) handles non-<html> strings via normalizeHtmlIfNeededGumboNormalizer.normalizeHtmlEnrichedParser.fromHtml, so tags/attributes are stripped and only the visible text remains.

MeasurementStore.getInitialText (measure) only parses when the string is already wrapped in <html>…</html>:

val text = props?.getString("text") ?: ""
val isHtml = text.startsWith("<html>") && text.endsWith("</html>")
if (!isHtml) return text   // returns RAW markup, unparsed

Since EnrichedText.tsx passes text={children} verbatim (no wrapper), external HTML falls into the !isHtml branch and the raw markup — including the full href attribute value — is laid out by StaticLayout as literal text. Measured height ≫ rendered height ⇒ trailing gap.

Reproduction

<EnrichedText useHtmlNormalizer>
  {'Hello,\n\n<a href="https://example.com/path?a=1&b=2&c=…(700+ chars)…">Link</a>'}
</EnrichedText>
  • Android: renders "Hello," + "Link", followed by a large empty gap whose size tracks the href length.
  • iOS: renders correctly with no gap.

Minimal trigger: any external HTML with substantial markup relative to visible text; a long href makes it obvious.

Expected

Android measurement should match the rendered content height (as iOS does).

Suggested fix

Have MeasurementStore.getInitialText normalize/parse external HTML the same way the render path does before measuring — i.e. when the string is not internal <html> and useHtmlNormalizer is set, run it through GumboNormalizer + EnrichedParser.fromHtml (mirroring EnrichedTextView.parseNormalizedHtml) rather than returning the raw string.

Workaround (for other users hitting this)

Wrap external HTML in <html>…</html> on Android only, which forces both the measure and render paths onto the internal-HTML parse branch:

const content = Platform.OS === 'android' ? `<html>${html}</html>` : html;

Verified to remove the gap while rendering identically.

Metadata

Metadata

Labels

bugSomething isn't working

Type

Fields

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions