To auto-resize a textarea in CSS, set field-sizing: content on it. The browser then sizes the box from the text inside it, growing a line at a time as the user types, and you cap the growth with min-block-size and max-block-size. Chrome 123, Firefox 152 and Safari 26.2 all support it, so current browsers need no JavaScript for it.
textarea {
field-sizing: content;
inline-size: 100%;
min-block-size: 3lh;
max-block-size: 12lh;
}
For most forms that is all you need. Each of the four lines is there for a reason, though, and dropping any one of them gives you a field that is too thin, too short or too tall.
What does field-sizing: content actually change?
Form controls are unusual boxes. A <div> takes its height from its content; a <textarea> does not. HTML gives it a “default preferred size”, worked out from the rows and cols attributes, and that size stays fixed whatever you type. The CSS Form Control Styling spec defines field-sizing against exactly that idea. With the initial value, fixed, the browser uses the default preferred size. With content, it “must ignore any default preferred size defined by the host language” and size the element from its content instead.
Once content is set, rows and cols stop doing anything. MDN states it plainly: “rows/cols have no effect on <textarea> elements with field-sizing: content set.” If your markup says rows="4" and you expected four lines as a minimum, you now get one.
An empty textarea collapses. The spec’s own note says it should fit “to a text caret”. With no width constraint the box is about as wide as the cursor and one line tall, which is why the snippet above sets both a width and a minimum height.
Why does my textarea grow sideways instead of down?
Because nothing stopped it growing sideways. A content-sized textarea gets wider first, and only once it reaches a width limit does it start wrapping and adding rows. MDN describes the order: if it is “unable to grow due to a width constraint”, it grows in height, and when it hits a height constraint it shows a scrollbar.
So for the usual full-width comment box, fix the inline size and let only the block size follow the content. That is what inline-size: 100% in the opening snippet does.
MDN also warns to “avoid setting a fixed width and height” with field-sizing: content, because they reimpose a fixed size. Read that as a warning against fixing both axes at once. Fixing the width alone is the pattern the spec uses in its own example, where a textarea with a set width has a height that “depends on the number of the content lines.”
The lh unit is the natural fit for the block limits. One lh is the element’s computed line height, so 3lh means three lines of text at whatever font size the field ends up with, which is what rows="3" used to promise. Chrome’s field-sizing guide uses the same approach, with rlh (the root line height) for the textarea minimum and ch for widths.
Padding and border behave as they do on any box. Under box-sizing: border-box they count towards the minimum, so 3lh of border box holds a little under three lines of text. Raise the minimum to 3.5lh if that matters; keep the padding.
How do I stop it growing forever?
Use max-block-size. Once the content passes that height the textarea stops growing and scrolls, exactly as a fixed-height one does. Without a maximum, someone pasting a long document gets a field taller than the viewport, and the submit button disappears off the bottom of the screen.
Pick the maximum from the layout. In a chat composer pinned to the bottom of the viewport, something like max-block-size: 40dvh keeps the message history visible. In an ordinary page form, 10 to 15 lines is usually plenty before scrolling inside the field becomes the better experience.
A growing field also pushes everything below it down the page. That does not count against your Cumulative Layout Shift score: web.dev’s CLS article says shifts “within 500 milliseconds of user input will have the hadRecentInput flag set, so they can be excluded from calculations.” Each keystroke that adds a line is that input.
Does placeholder text affect the size?
Yes. When the field is empty, the placeholder stands in as the content, so a long placeholder makes a wide or tall empty field and a short one makes a small field. The Chrome guide puts it as “the placeholder becomes the content.” With a fixed inline-size this mostly shows up as height: a two-sentence placeholder can give you a three-line empty textarea before anyone has typed. Keep placeholders short.
What about inputs and selects?
field-sizing is not textarea-only. It applies to “elements with default preferred size”, and MDN lists what that covers in HTML.
Text-type inputs (text, email, search, tel, url, number, password) grow in width as you type. The size attribute is ignored once content is set, and maxlength stops the growth when the character limit is reached. These need a min-inline-size, or an empty input is as narrow as the caret. They suit inline edit fields and tag inputs; a normal stacked form looks worse when every input is a different width.
A drop-down <select> resizes to fit whichever option is selected, instead of always being as wide as its longest option. A list box (multiple, or size greater than 1) grows tall enough to show every option without scrolling, and MDN notes it will “always display all the options of a listbox, even if size is smaller than the number of options.” That second behaviour is easy to trigger by accident with a broad selector, so scope the rule:
textarea,
input:is([type="text"], [type="search"]) {
field-sizing: content;
}
select:not([multiple]) {
field-sizing: content;
min-inline-size: 5ch;
}
File inputs shrink to the button plus the filename and grow when a longer filename is chosen. If you have restyled selects with appearance: base-select, the two properties work together; our post on customisable select elements covers the styling side.
Which browsers support field-sizing?
According to MDN’s browser compatibility data, field-sizing shipped in Chrome and Edge 123 (March 2024), Safari 26.2 (December 2025) and Firefox 152 (16 June 2026). That last release made it Baseline Newly available on 16 June 2026. As of October 2026 it is still months from Baseline Widely available, so a real share of visitors on older Safari and Firefox will not have it.
For a textarea that is a small problem. A browser that does not understand the property ignores it, and the field falls back to the fixed size from rows. The user gets a scrollbar instead of a growing box. Nothing breaks.
That is a good reason to keep rows in the markup. It does nothing where field-sizing is supported and gives the fallback a sensible starting height everywhere else.
Do I still need a JavaScript fallback?
Only if a growing textarea matters to the product, such as a chat or comment composer people use all day. If you do need one, feature-detect and do nothing in browsers that already have the CSS:
if (!CSS.supports("field-sizing", "content")) {
const fit = (el) => {
el.style.height = "auto";
const border = el.offsetHeight - el.clientHeight;
el.style.height = `${el.scrollHeight + border}px`;
};
for (const el of document.querySelectorAll("textarea[data-autosize]")) {
fit(el);
el.addEventListener("input", () => fit(el));
}
}
Resetting height to auto first lets the box shrink when text is deleted; otherwise scrollHeight never drops below the current height. scrollHeight includes padding but not the border, so the difference between offsetHeight and clientHeight is added back for border-box sizing. Your CSS min-block-size and max-block-size still clamp the result, so you keep one set of limits.
This listener only fires on user input. If your code sets el.value directly, for example when clearing the field after a send, call fit(el) yourself afterwards.
Neither version animates the growth. If you want each new line to ease in, that is the same problem as animating any box to height: auto, which our height: auto animation post covers.
What we would do
Ship field-sizing: content today, with inline-size: 100%, a min-block-size in lh and a max-block-size chosen from the layout, and keep rows in the HTML for older browsers. Delete the autosize library you were carrying, unless the field is central enough that Safari 26.1 users scrolling inside a fixed box is a real cost; then keep the short fallback above behind the CSS.supports check and plan to remove it once the feature reaches Baseline Widely available. Do not apply it to every select on the site without checking your list boxes first.
How forms behave on small screens is a regular part of our responsive web design work. If your forms fight the content they hold, we can help.