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 normalizeHtmlIfNeeded → GumboNormalizer.normalizeHtml → EnrichedParser.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.
Version
react-native-enriched-html@1.0.0, Android, New Architecture.Summary
When
EnrichedTextrenders external HTML (a string not wrapped in<html>…</html>) withuseHtmlNormalizer, 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 longhrefis 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 vianormalizeHtmlIfNeeded→GumboNormalizer.normalizeHtml→EnrichedParser.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>:Since
EnrichedText.tsxpassestext={children}verbatim (no wrapper), external HTML falls into the!isHtmlbranch and the raw markup — including the fullhrefattribute value — is laid out byStaticLayoutas literal text. Measured height ≫ rendered height ⇒ trailing gap.Reproduction
hreflength.Minimal trigger: any external HTML with substantial markup relative to visible text; a long
hrefmakes it obvious.Expected
Android measurement should match the rendered content height (as iOS does).
Suggested fix
Have
MeasurementStore.getInitialTextnormalize/parse external HTML the same way the render path does before measuring — i.e. when the string is not internal<html>anduseHtmlNormalizeris set, run it throughGumboNormalizer+EnrichedParser.fromHtml(mirroringEnrichedTextView.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:Verified to remove the gap while rendering identically.