React Textarea
Table of Contents + −
In the last lesson you learned about React Text Inputs, so you know how a single-line <input> works as a controlled component. This lesson is about the bigger box, the <textarea>, which works differently in React than in plain HTML, and we’ll use it to build a comment box with a live character counter.
🤔 Why is a textarea different from HTML?
A textarea in plain HTML and a textarea in React look the same, but you fill them in differently.
- In plain HTML, the text you start with goes between the tags, like
<textarea>hello</textarea>. So the text is the child of the element. - In React, you don’t do that. You set the text with the
valueprop instead, exactly like a normal input. - So the same
valueandonChangepattern you already know carries straight over. There’s nothing new to learn about the control itself.
So the only real change is where the text lives. In HTML it’s a child. In React it’s a prop.
✍️ A controlled textarea
Let’s see the wrong way and the right way side by side. The first is the HTML habit, the second is how React wants it.
import { useState } from "react";
function CommentBox() { const [text, setText] = useState("");
// ❌ Wrong - HTML style, putting text between the tags does nothing useful in React // <textarea onChange={(e) => setText(e.target.value)}>{text}</textarea>
// ✅ Right - use the value prop, just like an input return ( <textarea value={text} onChange={(e) => setText(e.target.value)} /> );}Let’s read what makes the right version work.
value={text}ties the box to your state, so what shows on screen is always whatevertextholds.onChange={(e) => setText(e.target.value)}runs on every keystroke and saves the new text back into state.- Putting the text between the tags like in HTML is the old habit. React ignores it for editing, so leave it out and use
value.
Don't put the text between the tags
In React, writing the starting text as a child like a HTML textarea won’t behave the way you expect. Always set the text with the value prop instead.
📐 The rows attribute
A textarea is a multi-line box, so you usually want to say how tall it should be. That’s what rows is for. This box starts five lines tall, which is comfortable for a comment.
<textarea value={text} onChange={(e) => setText(e.target.value)} rows={5}/>A couple of quick notes on rows.
rows={5}sets the visible height to about five lines of text. So it does not limit how much the user can type, it just sets the starting size.- The number goes inside curly braces because it’s a JavaScript value, not a plain string.
- If the user types more than five lines, the box gets a scrollbar. So the rows value is only the starting height, not a hard cap.
🔢 A live character counter
Now for a really useful thing. Because the text lives in state, you always know its length, so you can show a live count as the user types. This comment box shows how many characters the user has typed so far.
import { useState } from "react";
function CommentBox() { const [text, setText] = useState("");
return ( <div> <textarea value={text} onChange={(e) => setText(e.target.value)} rows={5} /> <p>{text.length} characters</p> </div> );}Here’s the flow, step by step.
- The user types, so
onChangesaves the new text intotext. - React re-renders, and
text.lengthreads the current number of characters. - The
<p>shows that count, and it updates by itself on every keystroke.
Output
(user types "Great lesson")
Great lesson12 charactersSo the count stays in sync for free. You never count anything by hand, you just read the length of the text in state and React keeps it fresh.
📉 Showing characters remaining
Lots of comment boxes have a limit, like “max 200 characters”. You can show how many are left with the same idea, just subtracting from a limit. This version sets a limit and shows the characters remaining as the user types.
import { useState } from "react";
function CommentBox() { const [text, setText] = useState(""); const limit = 200; const remaining = limit - text.length;
return ( <div> <textarea value={text} onChange={(e) => setText(e.target.value)} rows={5} maxLength={limit} /> <p>{remaining} characters left</p> </div> );}Let’s break this one down.
limitis your maximum, andremainingis justlimit - text.length. So it goes down as the user types.maxLength={limit}tells the browser to stop accepting input once the limit is reached, so the user simply can’t type past it.- The
<p>showsremaining, which counts down live and hits0when the box is full.
Two layers of safety
maxLength stops typing in the browser, and your remaining count tells the user where they stand. Using both gives a clear, friendly limit. You can also change the text color when remaining gets low to warn them.
🧩 What You’ve Learned
- ✅ In plain HTML a textarea’s text goes between the tags, but in React you use the
valueprop instead - ✅ A controlled textarea uses the same
valueandonChangepattern as a normal input - ✅ Read the typed text from
e.target.valueinsideonChangeand save it in state - ✅
rows={5}sets the starting height of the box, but doesn’t limit how much can be typed - ✅
text.lengthgives a live character count for free, because the text lives in state - ✅ Subtract from a limit to show characters remaining, and use
maxLengthto stop typing past it
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
How do you set the text of a textarea in React?
Why: Unlike plain HTML, a React textarea is controlled with the value prop, exactly like an input. Putting the text between the tags is the HTML habit and won't behave the way you expect.
- 2
Inside onChange, how do you read what the user typed in a textarea?
Why: e.target is the textarea that fired the event, so e.target.value is the current text. You usually save it into state with a setter.
- 3
What does the rows attribute do on a textarea?
Why: rows only sets the starting height of the box. If the user types more, the box just scrolls. To cap input, use maxLength.
- 4
How do you show a live character count for a controlled textarea?
Why: Because the text is in state, text.length always holds the current number of characters. React re-renders on each keystroke, so the count stays in sync by itself.
🚀 What’s Next?
Now you can handle a multi-line textarea as a controlled component and show a live counter. Next we’ll look at another kind of form control, where the user picks from a list of options instead of typing.