Making a textarea
dynamically resizable is fundamental to delivering a good UX on a text editor, a comment area or a get in a contact section. However, all the solutions available are too complex, require installing another dependency or need workarounds that are not a11y compliant.
In this post I'll show you a quick and simple solution for this problem using React, but you can apply it anywhere else.
Allright, show me the code then:
textarea {
width: 500px;
min-height: 100px;
padding: 8px;
font-size: 18px;
resize: none;
border: solid 1px;
border-radius: 4px;
outline: none;
}
const ref = useRef<HTMLTextAreaElement>(null);
const handleInput = (e: ChangeEvent<HTMLTextAreaElement>) => {
if (ref.current) {
ref.current.style.height = "auto";
ref.current.style.height = `${e.target.scrollHeight - 16}px`;
}
};
return (
<div className="App">
<section>
<textarea
ref={ref}
rows={1}
placeholder="Enter text here..."
onInput={handleInput}
/>
</section>
</div>
);
Explanation
I want to call your attention to some key points. Notice we're setting the height to auto
before setting it to scrollHeight-16
. Forget the -16
for now, I'll talk about it later. Let's just leave the line below to explain how it works.
const handleInput = (e: ChangeEvent<HTMLTextAreaElement>) => {
if (ref.current) {
ref.current.style.height = `${e.target.scrollHeight}px`;
}
};
What happens is that on every keypress, the textarea will increase it's height. If you notice you'll see that the height is increasing by 16px on every keypres. That happens because the scrollHeight include padding. To exemplify, let's say the height of the textarea is 40px initially, then what happens on each keypres is this:
i) height = x+16
ii) height = x+16+16
iii) height = x+16+16+16
...
That's why I subtracted the 16, because it's the sum of padding-top + padding-bottom
and I don't to count it over again.
So by now the height should be resizing correctly when the size is increasing but if you delete a line you'll see that the height don't go shorter. To fix that, I set the height to auto
before and it will do the job for us.
Also notice that I added the row={1}
. That's because I don't want to have an empty line below the last line. Try to remove it to see how it will add extra space at the bottom of the textarea.
That's it, hope it helps. Thanks.
Sandbox
https://codesandbox.io/s/dynamically-resizable-textarea-9jbs1y
References:
i) https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas
ii) https://www.npmjs.com/package/autosize
Top comments (2)
This is nice, i like this one 👍
Thanks! such a good simple solution