21

Any way to create a "rich" TextInput in React Native? Maybe not a full blown wysiwyg, but maybe just change the text color of various pieces of text; like the @mention feature on Twitter or Facebook.

nicholas
  • 14,184
  • 22
  • 82
  • 138
  • Did you figure out a solution? I'm trying to do something similar (only more like a rich-text editor with **bolding**, _italics_, etc). – chapeljuice Jan 11 '18 at 22:48
  • No. I never did find a solution. In my case I was trying to style @mentions with some kind of container or background color in the input. Ended up just removing that text from the input and putting it in another view above. Wasn't ideal, but it worked. – nicholas Jan 12 '18 at 20:12
  • 1
    @chapeljuice solution found - https://stackoverflow.com/a/49082641/1828637 – Noitidart Mar 05 '18 at 14:03
  • @nicholas its too late to ask.. but can you show a solution if possible – Sagar Chavada Sep 17 '18 at 07:21
  • @SagarChavada Noitidart's solution below worked perfectly. – nicholas Sep 19 '18 at 18:02
  • https://stackoverflow.com/q/52342466/5895830.. plz take a look on this one. – Sagar Chavada Sep 19 '18 at 18:06

4 Answers4

22

Solution is that you can use <Text> elements as children in a <TextInput> like this:

<TextInput>
    whoa no way <Text style={{color:'red'}}>rawr</Text>
</TextInput>
Noitidart
  • 35,443
  • 37
  • 154
  • 323
12

This question was asked a while ago but I think my answer can help other people looking for how to color the @mention part of a string. I am not sure if the way I did it is clean or the "react" way but here is how I did it: I take the inputed string and split it with an empty space as the separator. I then loop through the array and if the current item matches the pattern of @mention/@user, it is replaced with the Text tag with the styles applied; else return the item. At the end, I render inputText array (contains strings and jsx elements)inside the TextInput element. Hope this helps!

render() {
let inputText = this.state.content;
if (inputText){
  inputText = inputText.split(/(\s)/g).map((item, i) => {
    if (/@[a-zA-Z0-9]+/g.test(item)){
      return <Text key={i} style={{color: 'green'}}>{item}</Text>;
    } 
    return item;
  })
return <TextInput>{inputText}</TextInput>

Result

7

Have a look at the TokenizedTextExample from the react-native docs. I think that'll get you close to what you're looking to do. The relevant code follows:

class TokenizedTextExample extends React.Component {
  state: any;

  constructor(props) {
    super(props);
    this.state = {text: 'Hello #World'};
  }
  render() {

    //define delimiter
    let delimiter = /\s+/;

    //split string
    let _text = this.state.text;
    let token, index, parts = [];
    while (_text) {
      delimiter.lastIndex = 0;
      token = delimiter.exec(_text);
      if (token === null) {
        break;
      }
      index = token.index;
      if (token[0].length === 0) {
        index = 1;
      }
      parts.push(_text.substr(0, index));
      parts.push(token[0]);
      index = index + token[0].length;
      _text = _text.slice(index);
    }
    parts.push(_text);

    //highlight hashtags
    parts = parts.map((text) => {
      if (/^#/.test(text)) {
        return <Text key={text} style={styles.hashtag}>{text}</Text>;
      } else {
        return text;
      }
    });

    return (
      <View>
        <TextInput
          multiline={true}
          style={styles.multiline}
          onChangeText={(text) => {
            this.setState({text});
          }}>
          <Text>{parts}</Text>
        </TextInput>
      </View>
    );
  }
}
David Schumann
  • 13,380
  • 9
  • 75
  • 96
jopecko
  • 71
  • 1
  • 5
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes – slfan Sep 14 '16 at 16:32
  • 1
    Thanks for the feedback. I've updated the reply as suggested. – jopecko Sep 15 '16 at 15:23
  • Whoa I never knew you could put `` as children in a ``! This is super cool! This example doesn't seem to exist anymore on the RN docs, do you have any place I can find about this? – Noitidart Mar 03 '18 at 09:22
  • 1
    set as a children of don't work on IOS. Other tip: if you set the value property of it will give you a error because you can't have a children and value property in the same – Angel Mar 23 '18 at 16:42
5

You will have to use regex in order to achieve that behaviour. Someone has already created package for that have a look at react-native-parsed-text

This library allows you to parse a text and extract parts using a RegExp or predefined patterns. Currently there are 3 predefined types: url, phone and email.

Example from their github

<ParsedText
          style={styles.text}
          parse={
            [
              {type: 'url',                       style: styles.url, onPress: this.handleUrlPress},
              {type: 'phone',                     style: styles.phone, onPress: this.handlePhonePress},
              {type: 'email',                     style: styles.email, onPress: this.handleEmailPress},
              {pattern: /Bob|David/,              style: styles.name, onPress: this.handleNamePress},
              {pattern: /\[(@[^:]+):([^\]]+)\]/i, style: styles.username, onPress: this.handleNamePress, renderText: this.renderText},
              {pattern: /42/,                     style: styles.magicNumber},
              {pattern: /#(\w+)/,                 style: styles.hashTag},
            ]
          }
        >
          Hello this is an example of the ParsedText, links like http://www.google.com or http://www.facebook.com are clickable and phone number 444-555-6666 can call too.
          But you can also do more with this package, for example Bob will change style and David too. foo@gmail.com
          And the magic number is 42!
          #react #react-native
</ParsedText>
Nakib
  • 4,593
  • 7
  • 23
  • 45
  • 2
    That will help match patterns typed in the text, but the question was how might I then change the style of that text in the text input? Like the way facebook will highlight your mention's name with a blue background in the text field itself. – nicholas Jun 17 '16 at 20:47
  • 5
    You can do it by putting `ParsedText` inside `TextInput` component and removing the props `value` of the `TextInput`, after that the child of `ParsedText` just have to be set to the value you set in the `TextInput`, like that: ``` this.setState({text})}> {this.state.text} ``` – Jérémy Magrin Nov 24 '16 at 08:50
  • @JérémyMagrin I think `ParsedText` only works on Android, is this true? – Noitidart Jul 09 '18 at 00:42
  • @Nakib, hi there after 6 years :), i wonder how to apply to functional componet – CODEforDREAM Mar 01 '22 at 03:51