Simulate display: inline in React Native

CssReactjsFlexboxReact Native

Css Problem Overview


React Native doesn't support the CSS display property, and by default all elements use the behavior of display: flex (no inline-flex either). Most non-flex layouts can be simulated with flex properties, but I'm flustered with inline text.

My app has a container that contains several words in text, some of which need formatting. This means I need to use spans to accomplish the formatting. In order to achieve wrapping of the spans, I can set the container to use flex-wrap: wrap, but this will only allow wrapping at the end of a span rather than the traditional inline behavior of wrapping at word breaks.

The problem visualized (spans in yellow):

enter image description here

(via http://codepen.io/anon/pen/GoWmdm?editors=110)

Is there a way to get proper wrapping and true inline simulation using flex properties?

Css Solutions


Solution 1 - Css

You can get this effect by wrapping text elements in other text elements the way you would wrap a span in a div or another element:

<View>
  <Text><Text>This writing should fill most of the container </Text><Text>This writing should fill most of the container</Text></Text>       
</View>

You can also get this effect by declaring a flexDirection:'row' property on the parent along with a flexWrap: 'wrap'. The children will then display inline:

<View style={{flexDirection:'row', flexWrap:'wrap'}}>
  <Text>one</Text><Text>two</Text><Text>Three</Text><Text>Four</Text><Text>Five</Text>
</View>

Check out this example.

https://rnplay.org/apps/-rzWGg

Solution 2 - Css

I haven't found a proper way to inline text blocks with other content. Our current "hackish" workaround is to split every single word in a text string into its own block so flexWrap wraps properly for each word.

Solution 3 - Css

You can only nest text nodes without using flex to get the desired effect. Like this: https://facebook.github.io/react-native/docs/text

<Text style={{fontWeight: 'bold'}}>
  I am bold
  <Text style={{color: 'red'}}>
    and red
  </Text>
</Text>

Solution 4 - Css

I had the following use case:

I needed a text that can wrap with different sizes, and throughout that text, I wanted to underscore some of the words (to indicate that they are clickable).

It's quite simple expect for the case that you can't control the underline in any way (how close is it, what color is it, so on) - this led me through the rabbit hole, and eventually coming up with the solution of splitting every word, and wrapping it in separate Text component, wrapped with View.

I'll paste the code here:



import React from 'react';
import { StyleSheet, View, TouchableOpacity, Text } from 'react-native';
import Colors from '../../styles/Colors';
import Fonts from '../../styles/Fonts';

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

export default class SaltText extends React.Component {

  getTheme (type) {

    if (type === 'robomonoregular10gray') {
      return {
          fontSize: Fonts.SIZES.TEN,
          fontFamily: Fonts.ROBOTOMONO_REGULAR,
          color: Colors.getColorOpacity(Colors.GRAY, 70),
          lineHeight: Fonts.SIZES.TEN + 10
      };
    }

    throw new Error('not supported');
  }

  splitText (text) {
    const parts = [];
    const maps = [];

    let currentPart = '';
    let matchIndex = 0;

    for (const letter of text) {

      const isOpening = letter === '[';
      const isClosing = letter === ']';

      if (!isOpening && !isClosing) {
        currentPart += letter;
        continue;
      }

      if (isOpening) {
        parts.push(currentPart);
        currentPart = '';
      }

      if (isClosing) {
        parts.push(`[${matchIndex}]`);
        maps.push(currentPart);
        currentPart = '';
        matchIndex++;
      }
    }

    const partsModified = [];
    for (const part of parts) {
      const splitted = part
        .split(' ')
        .filter(f => f.length);

      partsModified.push(...splitted);
    }

    return { parts: partsModified, maps };
  }

  render () {

    const textProps = this.getTheme(this.props.type);
    const children = this.props.children;

    const getTextStyle = () => {
      return {
        ...textProps,
      };
    };

    const getTextUnderlineStyle = () => {
      return {
        ...textProps,
        borderBottomWidth: 1,
        borderColor: textProps.color
      };
    };

    const getViewStyle = () => {
      return {
        flexDirection: 'row',
        flexWrap: 'wrap',
      };
    };

    const { parts, maps } = this.splitText(children);

    return (
      <View style={getViewStyle()}>
        {parts.map((part, index) => {

          const key = `${part}_${index}`;
          const isLast = parts.length === index + 1;

          if (part[0] === '[') {
            const mapIndex = part.substring(1, part.length - 1);
            const val = maps[mapIndex];
            const onPressHandler = () => {
              this.props.onPress(parseInt(mapIndex, 10));
            };
            return (
              <View key={key} style={getTextUnderlineStyle()}>
                <Text style={getTextStyle()} onPress={() => onPressHandler()}>
                  {val}{isLast ? '' : ' '}
                </Text>
              </View>
            );
          }

          return (
            <Text key={key} style={getTextStyle()}>
              {part}{isLast ? '' : ' '}
            </Text>
          );
        })}
      </View>
    );
  }
}

and usage:

  renderPrivacy () {

    const openTermsOfService = () => {
      Linking.openURL('https://reactnativecode.com');
    };

    const openPrivacyPolicy = () => {
      Linking.openURL('https://reactnativecode.com');
    };

    const onUrlClick = (index) => {
      if (index === 0) {
        openTermsOfService();
      }

      if (index === 1) {
        openPrivacyPolicy();
      }
    };

    return (
      <SaltText type="robomonoregular10gray" onPress={(index) => onUrlClick(index)}>
        By tapping Create an account or Continue, I agree to SALT\'s [Terms of Service] and [Privacy Policy]
      </SaltText>
    );
  }

this is the end result:

example

Solution 5 - Css

Try this, simple and clean.

<Text style={{ fontFamily: 'CUSTOM_FONT', ... }}>
   <Text>Lorem ipsum</Text>
   <Text style={{ color: "red" }}>&nbsp;dolor sit amet.</Text>
</Text>

Result:

Lorem ipsum dolor sit amet.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBrent TrautView Question on Stackoverflow
Solution 1 - CssNader DabitView Answer on Stackoverflow
Solution 2 - CssKristfalView Answer on Stackoverflow
Solution 3 - CssbezoerbView Answer on Stackoverflow
Solution 4 - CssErti-Chris EelmaaView Answer on Stackoverflow
Solution 5 - CssAndriyFMView Answer on Stackoverflow