August 6, 2026
Auto-growing textarea

For twenty years, making a textarea grow along with its content meant JavaScript: measure the content, write back a height, and patch every case where that goes wrong. But the internet is evolving… and today it takes only one CSS property.

Both versions (JavaScript and CSS) are working on this page. Go ahead and try them out.

The JavaScript way

<textarea id="message" oninput="grow(this)"></textarea>
<style>
textarea {
    min-height: 2lh; 
    max-height: 12lh;
    resize: vertical;
}
</style>
<script>
    var message = document.getElementById('message');

    function grow(el) {
        if (el.dataset.resized) return;
        var style = getComputedStyle(el);
        var extra = style.boxSizing === 'border-box'
            ? parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth)
            : -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
        el.style.height = 'auto';
        el.style.height = el.scrollHeight + extra + 'px';
        el.dataset.height = el.style.height;
    }

    new ResizeObserver(function () {
        if (message.style.height !== (message.dataset.height || '')) {
            message.dataset.resized = 'yes';
        }
    }).observe(message);

    addEventListener('pageshow', function () { grow(message); });
    addEventListener('resize', function () { grow(message); });
</script>

This is a good piece of code. No dependencies, no build step, and it works in every browser released since 2020. A relatively modern take on the old JavaScript approach. This code is exact to the pixel, because it reads the real border and padding widths off the computed style instead of guessing at line heights. It starts at the right size, it survives the back button, and it holds up when your phone gets rotated.

It also stays out of the way once the visitor takes over. Dragging the resize grip stores the new height as an inline ‘height’ — the very property the script writes to. So the script notes down every height it sets, and the observer compares. A height the script did not write must be the visitor’s, and the growing switches off for good. Very user-friendly.

A good piece of code, but there is a better solution.

The CSS way

textarea {
    field-sizing: content;
    min-height: 2lh;
    max-height: 12lh;
    resize: vertical;
}

Your browser supports 'field-sizing: content', so the box above grows.

Your browser does not support 'field-sizing: content' yet, so the box above stays a plain textarea with a scrollbar. That is the fallback, and nothing is broken.

This is the modern way, and it is better. Not only because it is shorter.

The JavaScript version can easily break. The reason can be an error thrown by an unrelated script on the page, a request that never arrives, a Content Security Policy without ‘unsafe-inline’, an extension or just a user with JavaScript switched off. The CSS version cannot fail that way: a property either applies or it does not, and where it does not you are still left with a working textarea.

The JavaScript version can also cause layout shifts. The CSS version is sized by the browser during layout, which means it is already right at every moment: first paint, a value restored by the back button, a value set by your own code or by a password manager, a rotated phone, a dragged window and every other thinkable situation. If the script loads too late, you can end up with an annoying layout shift or a change in the layout while you are already typing in the textarea. A very bad experience. Note that YOU might not see the delay in the JavaScript version, but that does not mean it is not there on a slow connection or a busy/underpowered device.

Some remarks:

  1. A textarea with ‘field-sizing: content’ expands horizontally until it hits a width constraint, and only then starts adding rows. In an ordinary form the container already provides that constraint, but if yours does not, set ‘width: 100%’ — otherwise an empty field could collapse to a sliver.
  2. Support is good, but not excellent. Chrome and Edge have had it since March 2024, Safari since 26.2 in December 2025, and Firefox since 152 in June 2026 — the release that made it Baseline newly available. “Newly available” means some visitors are still on older browsers, and for them the textarea does not grow. It shows a scrollbar instead, exactly as textareas have worked since 1995 — and because ‘resize: vertical’ is still switched on, those visitors can drag the box bigger themselves.
  3. Note that ‘rows’ and ‘cols’ stop working. Once ‘field-sizing: content’ is set, those attributes have no effect at all. Use ‘min-height’ instead of ‘rows’.
  4. The compatibility worry is an easy win for the CSS approach. In an old browser the CSS version simply does not grow: a small disappointment. The JavaScript version can break. And even when it does not, a slow device or connection can make it shift the layout while someone is already typing… in the newest browser available. The graceful degradation CSS offers is superior.

Conclusion

The JavaScript solution is a fine piece of engineering. It did its job for twenty years, in many different versions and forms, but there is no longer a reason to ship it. Replace it with the (much simpler) CSS solution.

Happy coding!

()  Joost van der Schee