Textarea
Takes several lines of text and grows with them.
Examples
33 left
Near the character limitUp to 280 characters.
Shown to whoever takes the conversation next.
Roll back the release to edit these.
Height
three rowsdefault
one row
Corner
fixeddefault
resizable
States
rest
hover
focus-visible
read-only
disabled
invalid
33 over the 80 limit
Over the 80 character limitover limit
Usage
When to use it
- Use
- The answer runs past one line: instructions, a description, a note, a handoff message.
- Use
- Line breaks matter, like a list of steps or a pasted email thread.
- Not for
- The answer fits on one line. Use Input.
- Not for
- The text is an agent prompt with mentions and slash commands. Use Prompt input.
Rules
8 rules
- Must
- Use limit, not maxLength. Because maxLength cuts a long paste without a word, while limit keeps every character and counts how far over it is.
- Should
- State the limit in FieldDescription; the counter appears only at 80%.
- Must
- Name the textarea with FieldLabel or aria-label. Because a screen reader announces an unnamed text area, and the placeholder is gone once the person types.
- Must
- Size with minRows and maxRows; never a fixed h- class. Because a fixed height stops the growth from minRows to maxRows, so a long note scrolls inside a short box.
- Should
- Set maxRows between 5 and 12 for text that can run long.
- Should
- Write the placeholder as one real example sentence under 100 characters.
- Never
- Add your own aria-live counter to a Textarea with limit. Because it speaks on every keystroke, on top of the 80% and over-limit crossings the Textarea already announces.
- Never
- Submit a form on Enter in a Textarea; Enter adds a line. Because people press Enter for a new paragraph, so the form submits a half-written note.
Accessibility
6 checks
- The textarea is a multi-line textbox with aria-invalid and aria-describedby to the description, error and counter.
- The counter is sr-only under 80% of limit and linked by aria-describedby.
- A polite status region announces only the crossings: near the limit and over it.
- Enter adds a line and never submits; Tab leaves and inserts no tab character.
- Text is 16px on coarse pointers so iOS does not zoom.
- Forced colors draw a 1px system border; the over-limit words carry the state.
Edge cases
10 edge cases
- 2,000-character paste or long URL
- Grows to maxRows then scrolls inside; an unbroken URL wraps anywhere.
- One character
- Sits in a three-line box; it never shrinks below minRows.
- Container 120px
- The box fills the width and the counter wraps under it.
- Right to left
- Text aligns right and the counter sits at the inline end.
- Zoom 200%
- Heights are in lh, so three lines stay three lines as the text grows.
- In a well
- Stays paper with its control edge, so it reads on the grey.
- Empty or whitespace only
- Empty shows the placeholder; whitespace counts toward the limit.
- Held key past the limit
- Every character stays and the counter keeps pace; the region speaks only on crossing.
- Failed submit
- Keep every character.
- Reduced motion
- Growth is a cut, not a tween.
API
6 props
minRowsnumberdefault3maxRowsnumberdefault12limitnumberresizablebooleandefaultfalseonValueChange(value: string) => voidclassNamestring
Tokens
17 tokens
--line-control- oklch(var(--ink-c) / 0.16)
--radius-soft- 6px
--type-label- 400 14px/16px var(--font-sans)
--space-2- 8px
--line-frame- oklch(var(--ink-c) / var(--line-frame-alpha))
--dur-fast- 120ms
--fade-edge- 24px
--line-focus- 2px
--focus-halo- 0 0 0 3px oklch(var(--ink-c) / 0.08)
--border-focus- var(--ink)
--focus-offset- 2px
--accent-wash- var(--surface-selected)
--accent-mark- var(--ink)
--type-label-sm- 400 12px/16px var(--font-sans)
--text-3- oklch(var(--ink-c) / 0.66)
--text-1- var(--ink)
--fail-text- oklch(51% 0.180 26)