How would I grow <TextInput> height upon text wrapping?

React Native

React Native Problem Overview


I'm trying to create a <TextInput> that can grow in height when the text wraps to the next line, similar to how Slack's message input grows with the text up to a point.

Slack input

I have the multiline prop set, so it is wrapping but the docs don't seem to mention any event regarding wrapping, and the only thing I can think of is a really hacky strategy to character count to figure out when to increase height of the input. How would I accomplish this? https://facebook.github.io/react-native/docs/textinput.html

React Native Solutions


Solution 1 - React Native

Thanks to react-native doc: https://facebook.github.io/react-native/docs/textinput.html

You can do something like that:

class AutoExpandingTextInput extends React.Component {
  state: any;

  constructor(props) {
    super(props);
    this.state = {text: '', height: 0};
  }
  render() {
    return (
      <TextInput
        {...this.props}
        multiline={true}
        onChange={(event) => {
          this.setState({
            text: event.nativeEvent.text,
            height: event.nativeEvent.contentSize.height,
          });
        }}
        style={[styles.default, {height: Math.max(35, this.state.height)}]}
        value={this.state.text}
      />
    );
  }
}

0.46.1 or higher: (as explained by Nicolas de Chevigné)

class AutoExpandingTextInput extends React.Component {

  constructor(props) {
    super(props);
    this.state = {text: '', height: 0};
  }
  render() {
    return (
      <TextInput
        {...this.props}
        multiline={true}
        onChangeText={(text) => {
            this.setState({ text })
        }}
        onContentSizeChange={(event) => {
            this.setState({ height: event.nativeEvent.contentSize.height })
        }}
        style={[styles.default, {height: Math.max(35, this.state.height)}]}
        value={this.state.text}
      />
    );
  }
}

Solution 2 - React Native

Since React Native 0.46.1 :

> contentSize property was removed from TextInput.onChange event

If you use this version, you can deal with onContentSizeChange prop

From the Jérémy answer, we have

class AutoExpandingTextInput extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
        text: '',
        height: 0
    };
  }

  render() {
    return (
      <TextInput
        {...this.props}
        multiline={true}
        onChangeText={(text) => {
            this.setState({ text })
        }}
        onContentSizeChange={(event) => {
            this.setState({ height: event.nativeEvent.contentSize.height })
        }}
        style={[styles.default, {height: Math.max(35, this.state.height)}]}
        value={this.state.text}
      />
    );
  }
}

Solution 3 - React Native

You should just set a maxHeight property in the style:

<TextInput multiline style={{maxHeight: 80}} />

Demo here: https://snack.expo.io/@yairopro/multiline-input-with-max-height

Solution 4 - React Native

My answer is to use onContentSizeChange and numberOfLines props in TextInput, of course turn on multiline, this is my solution:

let numOfLinesCompany = 0;
<TextInput
     ...
     multiline={true}
     numberOfLines={numOfLinesCompany}
     onContentSizeChange={(e) => {
         numOfLinesCompany = e.nativeEvent.contentSize.height / 18;
     }}
/>

18 is the height of text, propably depends on fontSize

Solution 5 - React Native

<TextInput multiline style={{maxHeight: ...}} />

Solution 6 - React Native

What I have done is set View for the Input max height, for example:

messageBox: {
maxHeight: 110,
width: '80%',
borderRadius: 10,
padding: 10,
backgroundColor: Color.White},

and set the Input to multiline:

<View style={[styles.messageBox, styles.managerMsg]}>
  <Input
    allowFontScaling={false}
    name="notes"
    blurOnSubmit={false}
    onChangeText={notes => console.log(notes)}
    returnKeyType="go"
    onSubmitEditing={() => Keyboard.dismiss()}
    multiline
  />
</View>

This "Input" is just a simple custom component that receives all Props.

Solution 7 - React Native

You might as well try something like this.

<TextInput style={[styles.inputs,{height: Math.max(35, this.state.height)}]}
                placeholder="Write a message..."
                multiline={true}                
                onChangeText={(msg) => this.setState({ text: msg })}
                onSubmitEditing={Keyboard.dismiss}
                onContentSizeChange = {() => this.scrollView.scrollToEnd({animated:true})}
               
              />

style.inputs

  inputs: {
    
    minHeight:40,
    marginLeft: 16,
    paddingTop: 10,
    overflow:'hidden',
    padding: 15,
    paddingRight: 25,
    borderBottomColor: '#000000',
    flex: 1,
    position: 'absolute',
    width: '100%',


  },

When the text grows beyond one line, a scroll view is created within the text input field and scrolls automatically to the bottom. This actually doesn't increase the text input height when the content grows, instead, a scroll view is created within the input field. Serves the purpose. I'm posting my solution as others didn't work for me(the text input size grew behind the keyboard, and there were text-overflow issues). Hence, for anyone who faced a similar problem as I did, this would hopefully work for you.

Solution 8 - React Native

<View style={{flex:1}}>
    <TextInput
        multiline={true} />
</View>

Solution 9 - React Native

You can also use onChangeText to set the TextInput height like so.

  const textHeight = 19 //fontsize of the text
  <TextInput
  style={[styles.input, 
         {height:Math.max(40,inputHeight)}]}

  onChangeText= {text => {
    const height = text.split("\n").length
    setInputHeight(height * textHeight)
  }}

  multiline
/>

Solution 10 - React Native

Just use multiline={true} no need to set height. check the following

<SafeAreaView style={{flex:1, backgroundColor:"#F8F8F9"}}>
        <ScrollView style={{flex:1}}>
            {
                this.state.data && <CommentListView data={this.state.data} onReplyClick={this.onReplyClick}/>
            }
        </ScrollView>
        <View style={{bottom:Platform.OS=="ios" ? this.state.keyboardHeight : 0, flexDirection:"row", alignItems:"flex-end", padding:10}}>
            <TextInput 
            multiline={true}
            style={{flex:3, backgroundColor:"#EFEFEF", borderRadius:5, padding:10}} 
            value={this.state.comment} 
            onChangeText={(text)=> this.setState({comment:text})}
            />
            <TouchableOpacity style={{flex:1}} onPress={()=> this.submit()}>
                <Text style={{padding:13, color:"#fff", textAlign:"center", fontWeight:"bold", backgroundColor:"#00394D", borderWidth:1, borderColor:"#00FFEE", borderRadius:5, overflow: "hidden"}}>Post</Text>
            </TouchableOpacity>
        </View>
    </SafeAreaView>

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
QuestionRyan McDermottView Question on Stackoverflow
Solution 1 - React NativeJérémy MagrinView Answer on Stackoverflow
Solution 2 - React NativeNicolas de CView Answer on Stackoverflow
Solution 3 - React NativeYairoproView Answer on Stackoverflow
Solution 4 - React NativeTuan Dat TranView Answer on Stackoverflow
Solution 5 - React NativeMachouView Answer on Stackoverflow
Solution 6 - React NativeMorenoMdzView Answer on Stackoverflow
Solution 7 - React NativePrajwal KulkarniView Answer on Stackoverflow
Solution 8 - React NativeddaaggeettView Answer on Stackoverflow
Solution 9 - React NativedevGrammerView Answer on Stackoverflow
Solution 10 - React NativevickyView Answer on Stackoverflow