HTML Textarea Element
The text input is a single line. It is perfect for a name or a city, but a message, a review, a bio, or a comment needs room to breathe, and room for line breaks. For these, HTML has the <textarea> element: a resizable box for as much text as the visitor wants to write.
It looks easy, but a textarea is not an <input>. It has its own tag, its own way of setting a starting value, and a few small surprises about line breaks and length limits. In this lesson, you will learn them all, and how to show what visitors write safely.
What You Will Learnβ
- How
<textarea>differs from<input type="text"> - Setting size with
rows,cols, and CSS - Setting a starting value (it goes between the tags)
- Length limits, a live counter, and an auto-growing box
- How line breaks are sent and how to show them
- Styling, accessibility, security, and common mistakes
The Basicsβ
- HTML Code
- Browser Output
<label for="message">Your message</label>
<textarea id="message" name="message" rows="4" cols="40"></textarea>
What you get:
- A multi-line box.
Enteradds a new line, and does not submit the form. - A scrollbar appears when the text is longer than the box.
- In most browsers, a resize handle in the bottom-right corner lets the visitor drag the box bigger.
Tabmoves focus out of the box to the next control, as with other fields.
Textarea Is Not an Inputβ
Beginners often try to write <textarea type="text" />. That does not work. Here are the differences to remember:
<input type="text"> | <textarea> | |
|---|---|---|
| Tag | Self-closing (no end tag) | Needs an end tag: </textarea> |
| Lines | One | Many |
| Starting value | The value attribute | The text between the tags |
type attribute | Yes | No |
| Size | size (characters wide) | rows and cols |
pattern | Yes | No |
| Pasted line breaks | Removed | Kept |
<textarea /> does not work like other self-closing tags in HTML. The browser treats everything after <textarea> as the box's text until it finds </textarea>, so your page can "disappear" into the box. Always write <textarea ...></textarea>.
Setting the Sizeβ
rows and colsβ
rowsis the number of visible lines (the default is 2).colsis the visible width, in average characters (the default is 20).
<textarea name="bio" rows="6" cols="50"></textarea>
These are simple and work without any CSS. They are a good starting point, since rows also sets a sensible minimum height.
CSS: the Better Way to Sizeβ
For responsive designs, use CSS for the width, and keep rows for the height:
textarea {
width: 100%;
max-width: 40rem;
min-height: 8rem;
padding: 0.6rem 0.75rem;
border: 1px solid #64748b;
border-radius: 6px;
font: inherit;
font-size: 1rem;
line-height: 1.5;
}
Controlling Resizeβ
textarea {
resize: vertical; /* only taller or shorter: usually the best choice */
}
| Value | Effect |
|---|---|
both | Drag in any direction (the usual default) |
vertical | Only the height can change. Keeps your layout from breaking |
horizontal | Only the width can change |
none | No handle at all |
resize: vertical is usually the friendliest choice. Turn resizing off (none) only when you have a good reason, such as an auto-growing box, because many visitors like to enlarge a box that is too small.
Setting a Starting Valueβ
A textarea's starting text goes between the tags, and not in a value attribute.
- HTML Code
- Browser Output
<label for="poem">Write two lines</label>
<textarea id="poem" name="poem" rows="3" cols="30">Roses are red
Violets are blue</textarea>
A few small rules:
- Whitespace matters. Spaces and line breaks between the tags become part of the text. If you indent the content, the visitor sees those spaces, so start the text right after
>, as above. - A first line break is ignored. One new line straight after
<textarea>is dropped, which lets you start the text on its own line in your code. - Tags are not read as HTML inside it.
<b>is shown as the characters<b>. But you must still escape&and<when your server writes the starting text into the page (for example,<), and never let the text contain</textarea>, which would close the box early. - Reset restores it. A reset button brings back this starting text.
In React or other frameworks, you use defaultValue or value rather than children, as in the demos here.
Placeholder and Labelβ
<label for="review">Your review</label>
<textarea
id="review"
name="review"
rows="5"
placeholder="What did you like? What could be better?"
></textarea>
The placeholder can even contain line breaks in most browsers. But as with all inputs, a placeholder is not a label. It vanishes when the visitor starts typing, and is often too pale to read. Always add a real <label>, as you learned in Label Element.
Try It: A Character Counterβ
Long text often needs a limit: a tweet-sized bio, a review, an SMS. Set maxlength, and tell the visitor how much room is left. Type in the box.
- HTML Code
- Browser Output
<label for="message">Your message</label>
<textarea
id="message"
name="message"
rows="4"
cols="40"
maxlength="200"
aria-describedby="count"
></textarea>
<p id="count">200 of 200 characters left</p>
const box = document.getElementById("message");
const count = document.getElementById("count");
function update() {
const left = box.maxLength - box.value.length;
count.textContent = left + " of " + box.maxLength + " characters left";
}
box.addEventListener("input", update);
update();
Length Attributesβ
| Attribute | What it does |
|---|---|
maxlength | Stops typing at this many characters |
minlength | The fewest characters allowed. Checked when the form is submitted |
required | Cannot be left empty |
<textarea name="review" minlength="20" maxlength="500" required></textarea>
You will study these in Minlength and Maxlength and Required.
- Line breaks: The browser counts a new line as one character, but the form is sent with a two-character line break (
\r\n). So text that fitsmaxlengthin the browser can be a little longer on the server. Allow some slack in server limits. - Emoji and some scripts: The count is by "UTF-16 units," so one emoji (and some letters) may count as two. A counter and a limit may differ slightly from what people expect, so avoid being too strict.
- Counter announcements: Do not announce the counter on every key press to screen readers. Link it with
aria-describedby, and consider a polite live message only when the visitor is near the limit.
Try It: A Box That Growsβ
A fixed-height box can hide most of a long message. An auto-growing textarea expands as the visitor types. Type several lines in the box.
- HTML Code
- Browser Output
<label for="notes">Notes</label>
<textarea id="notes" name="notes" rows="2"></textarea>
textarea {
width: 100%;
max-width: 24rem;
resize: none;
overflow: hidden;
}
const box = document.getElementById("notes");
box.addEventListener("input", () => {
box.style.height = "auto"; // shrink first, so it can get smaller
box.style.height = box.scrollHeight + "px"; // then fit the content
});
Newer browsers can do this with no JavaScript:
textarea {
field-sizing: content; /* grows and shrinks with the text */
min-height: 3rem;
max-height: 20rem;
}
Not every browser supports field-sizing yet, so keep the JavaScript version as a fallback if you need it everywhere. Set a max-height so a very long text does not push the rest of the page away.
Line Breaks and Wrappingβ
How Line Breaks Are Sentβ
When a form is submitted, each new line in the box becomes a carriage return and line feed (\r\n). On the server, you receive the text with those breaks inside it. You can normalize them to a single \n.
- HTML Code
- Browser Output
<form>
<label for="poem">Write two lines</label>
<textarea id="poem" name="poem" rows="3" cols="30">Roses are red
Violets are blue</textarea>
<button type="submit">Send</button>
</form>
In the demo, FormData reads the value with \n line breaks (the browser's own form, before it is encoded for sending). In an actual submission, the data on the wire has \r\n.
The wrap Attributeβ
| Value | What is sent |
|---|---|
soft (the default) | The text exactly as typed. The box wraps long lines for display only |
hard | Line breaks are added wherever the box wrapped the text. Needs cols |
Almost always, leave wrap at its default. wrap="hard" changes your visitor's text by adding breaks they never typed, and the result depends on the box's width.
Showing the Text Back Safelyβ
When you show a visitor's message on a page (a comment, a review, a profile), two things must be handled.
Keep the line breaks visibleβ
HTML collapses line breaks into spaces. The easiest way to display them is CSS:
.comment {
white-space: pre-wrap; /* keeps line breaks, and still wraps long lines */
}
Escape the textβ
The text is untrusted. It can contain <script>, or a tag that breaks your layout. Insert it as text, not as HTML:
// Safe: treated as plain text
element.textContent = message;
// Dangerous: treated as HTML
element.innerHTML = message; // never with visitor text
On the server, use your template engine's automatic escaping. If you let visitors use formatting (bold, links), use a trusted sanitizing library or a safe format like Markdown with a sanitizer. You will study this in Secure Form Design.
All the Attributesβ
| Attribute | What it does | Example |
|---|---|---|
name | The key sent to the server | name="message" |
id | Connects the label | id="message" |
rows, cols | Visible size | rows="5" cols="40" |
placeholder | A short hint (never a label) | placeholder="Tell us more" |
maxlength, minlength | Length limits | maxlength="500" |
required | Cannot be empty | required |
readonly | Shown and sent, but cannot be edited | readonly |
disabled | Greyed out, and not sent | disabled |
wrap | soft (default) or hard | wrap="soft" |
autocomplete | Autofill hint | autocomplete="street-address" |
autocapitalize | Capitalization on phones | autocapitalize="sentences" |
spellcheck | Red underlines on or off | spellcheck="true" |
inputmode | Phone keyboard hint | inputmode="text" |
dirname | Also sends the text direction | dirname="message.dir" |
autofocus | Focus on load | autofocus |
Many of these work just like on text inputs. For addresses, autocomplete="street-address" is a good fit for a textarea, since street addresses often have several lines. Attributes that do not apply: type, value, pattern, min, max, step, size, and list.
Styling a Textareaβ
textarea {
display: block; /* removes the small gap under inline boxes */
width: 100%;
max-width: 40rem;
min-height: 8rem;
padding: 0.75rem;
border: 1px solid #64748b;
border-radius: 6px;
font: inherit; /* by default, textareas use a monospace font */
font-size: 1rem;
line-height: 1.5;
resize: vertical;
}
textarea:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 2px;
}
textarea::placeholder {
color: #64748b; /* readable contrast */
}
textarea:invalid:not(:placeholder-shown) {
border-color: #b91c1c;
}
- Many browsers use a monospace font in textareas.
font: inheritmakes it match your page. font-sizeof at least 16px stops iPhones from zooming into the box when it is focused.- Keep the focus outline visible, and make sure the placeholder has enough contrast.
Accessibility at a Glanceβ
- Always add a label. Wrap the textarea, or use
forandid. - State the limits and help in text: "Up to 500 characters," linked with
aria-describedby. A placeholder is not enough. - Allow a comfortable size. Boxes that are too small make it hard to see and edit what you wrote. Use
resize: vertical, and a sensiblerowsormin-height. - Keep the keyboard behaviour.
Entermakes a new line, andTableaves the box. Do not trap focus. - Do not block pasting, and do not change what people type as they type.
- Make errors clear and specific: "Write at least 20 characters. You have written 8."
- Use a visible focus style, and enough contrast for the text, placeholder, and border.
- For counters, avoid announcing every key press to screen readers.
- Do not use
readonlyordisabledto display important text. Show it as ordinary text, so everyone can read and select it.
Security and Validation Notesβ
- Every message is untrusted. Validate length, strip unwanted control characters, and escape on output.
- Limit size on the server, too, with some slack for
\r\n. Without a limit, someone can post megabytes of text. - Store and display safely: use parameterized database queries, and automatic HTML escaping.
- Guard against spam. Public comment forms attract bots. Use rate limits and moderation, and use
rel="nofollow ugc"on any links in user text. - Handle private information. Free-text boxes often collect names, numbers, and health or financial details by accident. Tell visitors what you do with the text.
Global Audience Notesβ
- Every script and language must be accepted. Do not limit characters.
- Right-to-left text (Arabic, Hebrew, Persian) works automatically in a textarea. Add
dir="auto"to let the browser choose the direction from the first characters, which helps in mixed-language sites:
<textarea name="comment" dir="auto"></textarea>
- Character limits are not equal across languages. 200 characters is far more content in Chinese than in English, so avoid limits that are too tight, and measure what you need.
- IME input (typing characters through a helper, as in Japanese, Chinese, and Korean) has a "composition" stage. Do not run validation or counters in a way that interrupts it.
- Keep the labels and help text short so they translate easily.
Common Mistakes to Avoidβ
- Writing
<textarea />without an end tag. - Using
value="..."for the starting text. It goes between the tags. - Indenting the starting text, so visitors see extra spaces.
- Using
<input type="text">for a multi-line message. - No label, or only a placeholder.
- Using
pattern, which does not work on a textarea. - Printing the text with
innerHTMLor without escaping. - Showing the text back without
white-space: pre-wrap, so line breaks vanish. - Using
wrap="hard"without understanding that it changes the text. - Ignoring that browsers count a line break as one character, while the server sees two.
- Turning off resizing and making the box too small to be useful.
- No server-side length limit or validation.
Practice Timeβ
Try these in your editor:
- Build a labeled
<textarea>withrows="5"andcols="40". Type several lines. - Remove the end tag, and see what happens to the rest of your page.
- Put starting text between the tags, then try an indented version, and compare.
- Add
maxlengthandminlength, and test them. - Build the character counter.
- Make the box grow as you type (or try
field-sizing: content). - Set
resize: vertical, thennone, and compare the experience. - Submit the form with
method="get", and look for%0D%0Ain the address. - Show the text on a page with and without
white-space: pre-wrap. - Bonus: try typing
<b>hello</b>in the box, and display it withtextContent, then withinnerHTML. What is the difference?
Quick Recapβ
Click a question to check your answer.
1. How is a textarea different from <input type="text">?
A textarea holds multiple lines, needs an end tag, takes its starting text between the tags (not a value attribute), has no type or pattern, and is sized with rows and cols.
2. Where do you put a textarea's starting text?
Between <textarea> and </textarea>. Spaces and line breaks there are part of the text, so do not indent it.
3. What do rows and cols do?
They set the visible height (in lines) and width (in average characters). CSS width, min-height, and resize give finer, more responsive control.
4. How are line breaks sent to the server?
As \r\n (a carriage return and line feed). The browser counts a break as one character for maxlength, but the server receives two, so leave some slack.
5. How do you show a visitor's multi-line text safely on a page?
Insert it as text (with textContent, or automatic escaping on the server), never as HTML. Use CSS white-space: pre-wrap to keep the line breaks.
6. What does wrap="hard" do, and should you use it?
It adds real line breaks to the submitted text wherever the box wrapped it (and needs cols). It changes what the visitor wrote, so almost always keep the default soft.
Key Takeawaysβ
<textarea>is the multi-line text control. It needs an end tag, and has notypeorvalueattribute.- The starting text goes between the tags. Watch out for extra spaces and line breaks.
- Size with
rowsandcols, or better with CSS.resize: verticalis a friendly default, and auto-growing boxes use JavaScript orfield-sizing: content. - Use
maxlength,minlength, andrequired, and add a counter. Remember that line breaks and emoji count differently on the server. - Line breaks are sent as
\r\n. Show them withwhite-space: pre-wrap, and always escape visitor text on output. - Label it, describe its limits in text, use
font: inherit, and validate length and content on the server.
A textarea is for free writing. Next, learn how to offer a list of choices in a dropdown, in Select, or return to the Forms overview.