Skip to content
FormSubmit

HTML

HTML textarea in a form: attributes, CSS styling and auto-grow

How to use a textarea in an HTML form: rows, cols, maxlength and wrap, styling with CSS, auto-grow, a character counter, and sending the message to email.

6 min readUpdated Oct 9, 2026

To add a textarea to a form, use <textarea name="message"></textarea> inside the <form> with a <label> connected by for and id. Set rows for its height and maxlength to cap its length. It has no value attribute: any starting text goes between the tags. Style it with CSS using width: 100%, font: inherit and resize: vertical.

This guide covers every textarea attribute that matters, how to style it, how to make it grow as people type, and how to receive the message by email. It's part of the HTML form elements reference.

A basic textarea in a form

message-form.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
  <label for="email">Email</label>
  <input id="email" type="email" name="email" autocomplete="email" required>

  <label for="message">Message</label>
  <textarea id="message" name="message" rows="6" required></textarea>

  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Send</button>
</form>

The name is what gets submitted as the key, so name="message" sends message=.... Without a name, the browser skips the field entirely.

Textarea attributes

AttributeWhat it does
nameThe key the value is submitted under. Required for it to be sent.
rowsVisible height in lines of text. Default is 2.
colsVisible width in average characters. Default is 20. Usually overridden by CSS width.
placeholderHint text shown while empty. Not a replacement for a label.
maxlengthMaximum number of characters. The browser stops input beyond it.
minlengthMinimum number of characters. Checked on submit, only if the field isn't empty.
requiredThe form won't submit while the textarea is empty.
wrapsoft (default) or hard. Controls whether visual wraps become real line breaks on submit.
readonlyCan't be edited, but is still submitted.
disabledCan't be edited and is not submitted.
autocompleteUsually off for free-text messages, or a value like street-address for an address box.
spellchecktrue or false to toggle the browser spell checker.

Combine minlength with required if you want both a non-empty message and a minimum length. minlength on its own lets an empty textarea through.

html
<textarea id="message" name="message" rows="6"
  minlength="20" maxlength="2000" required></textarea>

Why there's no value attribute

<input> is a void element and stores its starting value in value. <textarea> has a closing tag, and whatever sits between the tags is its starting value:

html
<textarea name="notes">Delivery after 5pm, please.</textarea>

Two consequences catch people out:

  • Whitespace counts. If you format your HTML with a line break and indentation between the tags, that whitespace becomes the default content, and the placeholder won't show. Keep the tags on one line: <textarea name="notes"></textarea>.
  • In JavaScript, use .value, not .textContent or .innerHTML. Changing textContent changes the default, not what's currently in the box after someone has typed.

Label association

A textarea needs an accessible name, just like an input. Use a <label> with for matching the textarea's id. If you have help text, such as a length limit, connect it with aria-describedby so screen readers read it too:

html
<label for="message">Message</label>
<p id="message-hint">Up to 2,000 characters.</p>
<textarea id="message" name="message" rows="6" maxlength="2000"
  aria-describedby="message-hint"></textarea>

Styling a textarea with CSS

Browser defaults for textareas are small, use a monospace font in some browsers and ignore your page's font settings. A solid starting point:

textarea.css
textarea {
  box-sizing: border-box;   /* padding and border stay inside width: 100% */
  width: 100%;
  min-height: 8rem;
  padding: 0.625rem 0.75rem;
  font: inherit;            /* use the page font, not the browser default */
  line-height: 1.5;
  color: inherit;
  background: #fff;
  border: 1px solid #c4c9d4;
  border-radius: 0.5rem;
  resize: vertical;         /* taller, but never wider than the layout */
}

textarea:focus-visible {
  outline: 2px solid #4f46e5;
  outline-offset: 2px;
  border-color: #4f46e5;
}

textarea:user-invalid {
  border-color: #dc2626;
}

Notes on each part:

  • box-sizing: border-box stops width: 100% plus padding and border from overflowing its container.
  • font: inherit fixes the mismatched font. Form controls don't inherit font settings by default.
  • resize accepts none, both, horizontal or vertical. vertical is the usual choice. Avoid none unless the box auto-grows, because people reviewing a long message benefit from making it taller.
  • Keep a visible focus style. If you remove the default outline, replace it with something at least as clear.
  • :user-invalid only matches after the visitor has interacted with the field, so an empty required textarea isn't shown in red as soon as the page loads.

Auto-grow as the user types

With CSS: field-sizing

The field-sizing: content property makes a textarea size itself to its content, with no JavaScript:

css
textarea {
  field-sizing: content;
  min-height: 4lh;   /* at least 4 lines */
  max-height: 20lh;  /* then scroll */
}

Browser support isn't universal yet, so check current support before relying on it alone. Browsers that don't understand it ignore it and show a normal textarea, which is a safe fallback.

With JavaScript: a small fallback

For browsers without field-sizing, reset the height and set it to the content height on each input:

auto-grow.html
<textarea id="message" name="message" rows="3" data-autogrow></textarea>

<script>
  if (!CSS.supports("field-sizing", "content")) {
    document.querySelectorAll("textarea[data-autogrow]").forEach((el) => {
      const fit = () => {
        el.style.height = "auto";
        el.style.height = el.scrollHeight + "px";
      };
      el.addEventListener("input", fit);
      fit();
    });
  }
</script>

Pair this with box-sizing: border-box from the CSS above, otherwise the height can be off by the border width.

Add a character counter

maxlength stops input at the limit but gives no warning. A live counter helps people plan their message. Use aria-live="polite" so screen readers announce it without interrupting.

counter.html
<label for="message">Message</label>
<textarea id="message" name="message" rows="6" maxlength="500"
  aria-describedby="message-count"></textarea>
<p id="message-count" aria-live="polite">0 / 500</p>

<script>
  const box = document.getElementById("message");
  const count = document.getElementById("message-count");
  box.addEventListener("input", () => {
    count.textContent = `${box.value.length} / ${box.maxLength}`;
  });
</script>

Browsers count line breaks as one character for maxlength, although they're submitted as two (\r\n). If your server enforces an exact limit, allow a little slack.

How line breaks are submitted

When the form submits, every line break the visitor typed is sent as a CRLF pair (\r\n). Lines that only wrap visually because they're wider than the box are sent as one long line.

wrap="hard" changes that. With it, the browser inserts real line breaks wherever the text visually wraps, based on cols. It requires cols to be set and is rarely what you want for a message field, because the breaks depend on the box's width.

In a FormSubmit notification email, line breaks in the message are kept, so a multi-paragraph message reads as it was typed.

Send the textarea message to email

The textarea is often the whole point of a form: it's the message. To have that message arrive in your inbox, point the form's action at https://formsubmit.app/f/YOUR_FORM_ID with method="POST", as in the first example. Each submission is spam-filtered, stored in your dashboard and emailed to you, and replying answers the visitor directly when the form has an email field. The full setup is in how to send an HTML form to email, or start from a ready-made email form.

Frequently asked questions

How do I set a default value for a textarea?

Put the text between the opening and closing tags. A textarea has no value attribute, so value="..." is ignored. Leave nothing between the tags, not even a space or line break, if you want it empty.

How do I stop users resizing a textarea?

Set resize to none in CSS. A better option is usually resize vertical, which lets people make the box taller to review a long message without breaking your layout.

How do I make a textarea grow with its content?

Use the CSS property field-sizing with the value content where it is supported, and add a few lines of JavaScript that set the height to the scrollHeight on input as a fallback.

Are line breaks in a textarea submitted?

Yes. Line breaks the visitor types are sent as CRLF pairs. Soft wraps that only happen visually are not sent unless you set wrap to hard and give the textarea a cols value.

Why is my textarea not included in the submission?

It almost certainly has no name attribute, or it is disabled. The browser only submits fields with a name that are not disabled.

Keep reading

Your form backend is 60 seconds away

Sign up with Google or email, create a form, paste the endpoint. Free forever for small sites — no credit card.