Standard <textarea> does not autogrow while you add new rows:

text area without autogrow

Improved <textarea> with autogrow:

text area with autogrow

31.12.2024 version: #

// autogrow_controller.js
import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
  static values = {
    maxHeight: { type: Number, default: 15 }
  }

  connect() {
    this.resize();
  }

  resize() {
    const textarea = this.element;
    textarea.style.height = 'auto';
    textarea.style.overflowY = 'hidden';

    // while textarea scrollHeight > textarea offsetHeight & textarea offsetHeight < this.maxHeightValue (5) rows * height of 1 row
    while (textarea.scrollHeight > textarea.offsetHeight && textarea.offsetHeight < this.maxHeightValue * parseFloat(getComputedStyle(textarea).lineHeight)) {
      // increase height of textarea by one row
      textarea.style.height = `${textarea.offsetHeight + parseFloat(getComputedStyle(textarea).lineHeight)}px`;
    }
    // if textarea already has > maxHeight rows, activate scroll
    if (textarea.offsetHeight >= this.maxHeightValue * parseFloat(getComputedStyle(textarea).lineHeight)) {
      textarea.style.overflowY = 'auto';
    }
  }
}

14.06.2023 version: #

Just connect the below stimulus controller to a <textarea> and you’re good to go!

rails g stimulus autogrow

StimulusJS controller inspired by MDN HTMLTextAreaElement example:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    this.element.style.overflow = 'hidden';
    this.grow();
  }

  grow() {
    this.element.style.height = 'auto';
    this.element.style.height = `${this.element.scrollHeight}px`;
  }
}

Usage with html.erb:

<%= form.text_area :content,
                    # rows: 5,
                    data: { controller: 'autogrow',
                            action: "input->autogrow#grow" } %>

31.03.2022 version: #

// app/javascript/controllers/autogrow.js
import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="autogrow"
// <%= form.text_area :content, data: {controller: "autogrow" } %>
// <textarea data-controller="autogrow" name="article[content]"></textarea>

export default class extends Controller {
  initialize() {
    this.autogrow = this.autogrow.bind(this);
  }

  connect() {
    this.element.style.overflow = 'hidden';
    this.autogrow();
    this.element.addEventListener('input', this.autogrow);
    window.addEventListener('resize', this.autogrow);
  }

  disconnect() {
    window.removeEventListener('resize', this.autogrow);
  }

  autogrow() {
    this.element.style.height = 'auto';
    this.element.style.height = `${this.element.scrollHeight}px`;
  }
}

This old version is based on the fantastic @guillaumebriday’s stimulus-textarea-autogrow