Skip to content
SheetLink Forms

Building · 2026-08-06 · 8 min read · By Arden Talbot, founder of SheetLink

HTML form textarea: a complete guide

Sizing that actually works, the missing value attribute, wrap and line breaks, what the browser enforces and what it only suggests - ending in a contact form you can ship.

A ledger-styled illustration of a multi-line text box beside spreadsheet rows, with a typed message flowing into a delivered row.

The two-minute answer

The HTML form textarea element collects multi-line text: messages, feedback, delivery notes, anything a single-line <input> cannot hold. It is written as a pair of tags, and it needs a name attribute or the browser will leave it out of the submission entirely:

<form action="/contact" method="POST">
  <label for="message">Your message</label>
  <textarea id="message" name="message" rows="6"></textarea>
  <button type="submit">Send</button>
</form>

That is a complete, working example. rows="6" makes the box six text lines tall, the label is wired to the field through the id, and whatever the visitor types is submitted under the key message. The rest of this guide is the details: how sizing actually works, why there is no value attribute, which constraints the browser enforces and which it merely suggests, and what happens to line breaks after the form is submitted.

There is no value attribute

An <input> carries its initial value in a value attribute. A textarea does not. Its initial content goes between the opening and closing tags:

<textarea name="notes">Leave the package with the doorman.</textarea>

MDN's textarea reference spells out the split: in HTML the initial content lives between the tags, while in JavaScript you read and write the .value property. Putting value="..." on the tag itself does nothing at all.

This design creates two traps. First, every character between the tags is content, including the whitespace your code formatter adds. Put the closing tag on its own indented line and the field starts life holding a newline and a run of spaces, which can quietly defeat required because the field is no longer empty. Keep the tags touching unless you mean to prefill. Second, there is no self-closing form: the parser reads <textarea/> as an opening tag and swallows the markup after it as text, until it finds a real closing tag.

rows and cols, and their odd defaults

Leave the sizing attributes off and you get a strangely small box: per the MDN reference, rows defaults to 2 and cols to 20. rows is the number of visible text lines; cols approximates width in average character widths. Neither is a limit of any kind. They only set the rendered size, and the visitor can keep typing well past both.

In practice rows earns its keep and cols does not. Setting rows in the HTML gives the field a sensible height even before your stylesheet loads. Width is better handled in CSS, where it can be fluid instead of frozen at a character count: width: 100% on the textarea plus a max-width on the form container beats cols in almost every layout.

Sizing with CSS: resize and field-sizing

textarea {
  width: 100%;
  box-sizing: border-box;
  min-height: 8rem;
  resize: vertical;
  field-sizing: content; /* auto-grow where supported */
}

Three of these declarations do most of the work. width: 100% with box-sizing: border-box fills the container without the padding overflowing it. min-height takes over from rows once CSS loads. resize: vertical keeps the drag handle users expect but stops sideways resizing, which is the direction that breaks layouts. Reach for resize: none sparingly; typing a long message into a small box that cannot grow is miserable.

field-sizing: content is the newest piece: the textarea grows as the user types, with no JavaScript. MDN lists it as Baseline newly available since June 2026, so treat it as an enhancement layered over a sensible min-height rather than a replacement for one. Browsers that do not know the property simply ignore the line.

maxlength and minlength are enforced in the browser only

maxlength caps what can be typed and minlength blocks submission below a floor, both measured in UTF-16 code units, which is why an emoji counts as two. Pair minlength with required if the field must also not be empty, since an empty optional field passes minlength untouched.

The part that matters for anything real: these are browser-side conveniences, not guarantees. A script, a curl command, or a spam bot POSTing straight at your form action never sees them. A cap that actually has to hold, like a 2,000 character limit on messages, must be re-checked wherever the submission is received. Use the attributes for what they are good at, which is giving honest users immediate feedback instead of a rejection after they hit send.

placeholder, required, readonly, disabled

placeholder shows hint text while the field is empty. It is not a label: it vanishes on the first keystroke, leaving the visitor mid-message with no reminder of what the field wanted. Use it for a short example of the expected content, with a real <label> above it.

required works on textareas exactly as on inputs: an empty field blocks submission and matches the :invalid CSS pseudo-class, which is your styling hook.

readonly and disabled both stop editing but differ where it counts. A readonly field is still submitted with the form; a disabled one is dropped from the submission entirely. If you prefill a note the server needs to get back, readonly is the one you want. Two smaller attributes worth knowing: spellcheck="false" stops red squiggles under codes and IDs, and autocomplete="off" asks browsers not to autofill the field.

wrap, line breaks, and what actually gets submitted

The wrap attribute has three values. soft, the default, wraps text visually but submits only the line breaks the user actually typed. hard makes the browser insert real CR+LF pairs wherever the text visually wrapped, and requires cols to be set to take effect. off disables wrapping and scrolls horizontally.

Almost nobody wants hard. The submitted message arrives pre-chopped at an arbitrary column width that reflects the box the visitor happened to type in, not any structure in the text. Leave wrap alone unless you have a specific reason.

As for the line breaks users type: they are normalized to CR+LF pairs, and in a standard urlencoded submission each one travels as %0D%0A on the wire. Decoded on the receiving side, the message reads exactly as it was written. You do not need to do anything to preserve them.

Labels and accessibility

Every textarea needs a visible <label> connected by matching for and id attributes. The connection does two jobs: screen readers announce the label when the field gains focus, and clicking the label focuses the field, which enlarges the touch target on phones.

Placeholder text cannot substitute for this. It disappears while the person is answering, its default contrast is poor, and assistive technology support for treating it as a name is inconsistent. If the field needs more explanation than a label carries, put the hint in a paragraph and point aria-describedby at its id, so the extra context is read out along with the label. And when restyling, resist deleting the focus outline; replace it with your own visible style if the default clashes, because keyboard users navigate by it.

A complete contact form

Everything above, assembled into a form you can paste into any static page:

<form action="https://sheetlinkforms.com/f/YOUR_TOKEN" method="POST">
  <label for="name">Name</label>
  <input id="name" name="name" type="text" required>

  <label for="email">Email</label>
  <input id="email" name="email" type="email" required>

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

  <input name="_slhp" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">

  <button type="submit">Send</button>
</form>

Labels are wired to ids, required gives instant feedback, the maxlength is mirrored by a check at the receiving end, and the textarea's tags touch so the field starts genuinely empty. The hidden _slhp field is a honeypot: invisible to people, filled in by naive bots, and a cheap first spam layer that costs users nothing. The action URL decides where all of this goes. Pointing it at a hosted endpoint keeps the page fully static, a pattern the static site contact form guide covers end to end.

Where the message ends up

The textarea is the field where the destination shows its quality, because multi-line text is the easiest thing to mangle. Notification emails flatten formatting, and databases need a schema before the first message arrives. A spreadsheet handles it surprisingly well: a Google Sheets cell stores the line breaks intact, and turning on text wrap for the message column makes long messages readable in place.

Getting the submission into that spreadsheet is the part this site's product exists for. SheetLink Forms gives the form above its action URL: each submission lands as a row in Google Sheets or Excel Online, suspicious submissions are held for review instead of silently deleted, and values that look like formulas are defused before they can execute as one, the formula injection defense. If you would rather understand the plumbing before choosing a tool, the guide to connecting an HTML form to Google Sheets without a backend walks through every route, including the DIY ones.

FAQ

How do I set a default value in an HTML textarea?

Put the text between the opening and closing tags: <textarea name="notes">Default text</textarea>. The value attribute does not exist on textareas. From JavaScript, read or set the .value property as usual.

Why does my textarea start with blank spaces or an empty line?

Because whitespace between the tags is content. If your formatter indents the closing tag onto its own line, that newline and indentation become the field's initial value. Keep <textarea></textarea> adjacent with nothing between them.

How do I stop a textarea from being resized?

Set resize: none in CSS. The kinder middle ground is resize: vertical, which prevents the sideways dragging that breaks layouts while still letting people enlarge a box their message has outgrown.

Can a textarea grow automatically as the user types?

Yes, with field-sizing: content in CSS, no JavaScript needed. MDN lists it as Baseline newly available since June 2026, so keep a reasonable min-height as the fallback; older browsers ignore the property and keep the fixed size.

Does maxlength actually limit what gets submitted?

Only in the browser. It stops typing past the cap, but a bot or script posting directly to your form endpoint bypasses it entirely. Enforce any limit that matters at the receiving end as well.

What is the difference between readonly and disabled on a textarea?

Both prevent editing, but a readonly field is submitted with the form while a disabled one is left out of the submission completely. Prefilled content the server needs back should be readonly, not disabled.

Do line breaks in a textarea survive form submission?

Yes. Typed line breaks are normalized to CR+LF pairs and percent-encoded as %0D%0A in a urlencoded body. Once decoded on the receiving side, the message contains the same breaks the visitor typed.

Can I use the pattern attribute on a textarea?

No. pattern only works on certain <input> types. For structural rules on multi-line text, validate with a few lines of JavaScript before submit, and repeat the check wherever the submission is processed.

The form is the easy part

Point your form's action at a SheetLink endpoint and every message lands as a spreadsheet row - spam screened, held for review rather than dropped, free to start.

Start freeSee the live demo

Excel tables vs Google Sheets for form dataWhen to graduate from a spreadsheet