javascriptreactjsreact-nativereact-native-render-html

How to force a tag to full-width in React-Native-Render-Html?


We have a custom renderer for span tags but I can't find any way to force the span to full-width. I tried wrapping the custom renderer in a View as well as applying all the usual React-Native styles without luck.

Given input HTML like below

<span>Hello</span><span>Goodbye</span>

How can we render it as below?

Hello     
goodbye 

Currently it renders inline like:

hello goodbye


The custom renderer looks like this:


const renderers = {
  span: RenderedSpan,
};

const RenderedSpan = props => {
  return (
      <Text>
        Some hardcoded text
      </Text>
  );
};

I figured I'd finally found a solution for the issue when I came across an example

That inspired me to try this code to set the contentModel to block:

(still no dice)

const customHTMLElementModels = {
  span: defaultHTMLElementModels.span.extend({
    contentModel: HTMLContentModel.block,
  }),
};

Solution

  • React Native Capabilities

    React Native has roughly two display algorithms: flex, and text-inline. Any element which has a Text parent will be displayed with the text-inline algorithm, and all other elements will be displayed with the flex algorithm. The former severely limits the set of styles which will be supported, this is what you are stumbling on.

    Elements inside text-inline will have the following styles ignored:

    I suggest you play with a snippet like the one below to have a gist:

    import React from 'react';
    import {SafeAreaView, useWindowDimensions, View, Text} from 'react-native';
    
    export default function App() {
      const {width} = useWindowDimensions();
      return (
        <SafeAreaView style={{flex: 1}}>
          <Text>
            Hello world!
            <View style={{ /* play with padding, margins, flex... etc! */ }}>
              <Text>I'm inside a view!</Text>
            </View>
            How are you doing?
          </Text>
        </SafeAreaView>
      );
    }
    

    Solution 1: force width

    Hence, the only way we could achieve what you are looking for via styles would be to set its width to the passed contentWidth:

    import React from 'react';
    import {SafeAreaView, useWindowDimensions, View} from 'react-native';
    import RenderHTML, {
      CustomTextualRenderer,
      useContentWidth,
    } from 'react-native-render-html';
    
    const htmlContent = `
    <span>Hello</span><span>Goodbye</span>
    `;
    
    const RenderedSpan: CustomTextualRenderer = ({TDefaultRenderer, ...props}) => {
      const width = useContentWidth();
    
      return (
        <View style={{width}}>
          <TDefaultRenderer {...props} />
        </View>
      );
    };
    
    const renderers = {
      span: RenderedSpan,
    };
    
    export default function App() {
      const {width} = useWindowDimensions();
      return (
        <SafeAreaView style={{flex: 1}}>
          <RenderHTML
            renderers={renderers}
            source={{html: htmlContent}}
            contentWidth={width}
          />
        </SafeAreaView>
      );
    }
    

    LINK TO SNACK

    Solution 2: use Block content model

    You suggested setting the span content model to block and with the snippet you provided, it works great:

    import React from 'react';
    import {SafeAreaView, useWindowDimensions, View} from 'react-native';
    import RenderHTML, {
      defaultHTMLElementModels,
      HTMLContentModel,
    } from 'react-native-render-html';
    
    const htmlContent = `
    <span>Hello</span><span>Goodbye</span>
    `;
    
    const customHTMLElementModels = {
      span: defaultHTMLElementModels.span.extend({
        contentModel: HTMLContentModel.block,
      }),
    };
    
    export default function App() {
      const {width} = useWindowDimensions();
      return (
        <SafeAreaView style={{flex: 1}}>
          <RenderHTML source={{html: htmlContent}} contentWidth={width} customHTMLElementModels={customHTMLElementModels} />
        </SafeAreaView>
      );
    }
    
    

    The block content model will force the render engine to guarantee rendered span elements won't have any Text ascendants, thus securing a flex column display. LINK TO SNACK