For AI agents: the complete documentation index is available at https://a3s-lab.github.io/UI/en/llms.txt, the full documentation bundle is available at https://a3s-lab.github.io/UI/en/llms-full.txt, and this page is available as Markdown at https://a3s-lab.github.io/UI/en/components/textarea.md.
Textarea captures one plain-text value whose line breaks are meaningful. It preserves native multiline editing while keeping large pastes, validation, and availability states contained.
Release note
Include the result, how it was verified, and what to do if verification
fails. Use no more than 320 characters.
Use class="textarea" on a native <textarea>. In production forms, compose it with Field so a persistent label, ordinary guidance, length policy, and current error remain in one relationship.
<div class="field"> <label for="release-note">Release note</label> <textarea id="release-note" class="textarea" name="releaseNote" rows="5" maxlength="320" aria-describedby="release-note-help" ></textarea> <p id="release-note-help" data-field-description> Include verification evidence and a rollback path. </p></div>
Use Textarea for native plain text. Use an editor when the value needs document structure, formatting, or syntax behavior; use Task Composer when the task also needs mentions, attachments, tools, and submission state.
Empty and ready are ordinary value states. Invalid begins only after validation has run. Disabled and read-only express different native availability contracts and must not be presented as interchangeable styling variants.
The default Textarea is a stable editing viewport with internal overflow and vertical native resize. It does not use unbounded content sizing. A product that deliberately adds auto-growth must cap the height, retain an internal scroll fallback, and prove that paste, undo, IME composition, and responsive transitions do not move unrelated controls.
rows chooses a useful initial viewport. Large pastes remain fully editable without increasing the page height indefinitely.
Keep a required Textarea neutral before the first validation attempt. On failure, preserve every line, identify the missing requirement, focus the Textarea, and add the error to its description relationship. Clear stale error semantics as soon as the value is valid or the form resets.
Release handoff
<form
class="grid w-full gap-4"
data-textarea-validation-demo="en"
novalidate
onsubmit="event.preventDefault(); const textarea = this.querySelector('#textarea-validation-en'); const field = textarea.closest('.field'); const error = this.querySelector('#textarea-validation-error-en'); if (textarea.checkValidity()) return; field.dataset.invalid = 'true'; textarea.setAttribute('aria-invalid', 'true'); textarea.setAttribute('aria-describedby', 'textarea-validation-help-en textarea-validation-error-en'); error.hidden = false; textarea.focus()"
onreset="const textarea = this.querySelector('#textarea-validation-en'); const field = textarea.closest('.field'); const error = this.querySelector('#textarea-validation-error-en'); const count = this.querySelector('#textarea-validation-count-en'); field.removeAttribute('data-invalid'); textarea.removeAttribute('aria-invalid'); textarea.setAttribute('aria-describedby', 'textarea-validation-help-en'); error.hidden = true; count.textContent = '0 / 320 characters'"
>
<div class="field">
<label for="textarea-validation-en">Release handoff note</label>
<textarea
id="textarea-validation-en"
class="textarea"
name="releaseHandoff"
rows="5"
minlength="40"
maxlength="320"
required
aria-describedby="textarea-validation-help-en"
oninput="const count = this.parentElement.querySelector('[data-validation-count]'); count.textContent = this.value.length + ' / 320 characters'; if (event.isComposing || !this.checkValidity()) return; const field = this.closest('.field'); field.removeAttribute('data-invalid'); this.removeAttribute('aria-invalid'); this.setAttribute('aria-describedby', 'textarea-validation-help-en'); field.querySelector('[role=alert]').hidden = true"
></textarea>
<p id="textarea-validation-help-en" data-field-description>
Use 40–320 characters to name the verification result and rollback path.
</p>
<output
id="textarea-validation-count-en"
class="text-muted-foreground text-sm"
data-validation-count
for="textarea-validation-en"
aria-label="Character count"
aria-live="off"
>
0 / 320 characters
</output>
<p
id="textarea-validation-error-en"
data-field-message
role="alert"
hidden
>
Add the verification result and the rollback steps to use if it fails.
</p>
</div>
<div class="flex flex-wrap gap-2">
<button class="btn" type="submit">
Save handoff
</button>
<button class="btn" type="reset" data-variant="outline">
Reset
</button>
</div>
</form>
Client validation improves recovery but never replaces server validation, sanitization, authorization, or stale-response handling. Do not validate, transform, persist, or submit while an IME composition is active.
Disabled removes the value from focus, editing, constraint validation, and FormData. Read-only keeps the value focusable, selectable, copyable, scrollable, and submitted. Both keep full text opacity and a visible reason.
Availability semantics
<form class="grid w-full gap-5" data-textarea-availability="en">
<div class="field" data-disabled="true">
<label for="textarea-disabled-en">Archived release note</label>
<textarea
id="textarea-disabled-en"
class="textarea"
name="archivedReleaseNote"
rows="4"
aria-describedby="textarea-disabled-reason-en"
defaultValue="This note is retained for audit history after the workspace was archived."
disabled
/>
<p id="textarea-disabled-reason-en" data-field-description>
Archived workspaces keep their history but cannot change this value.
</p>
</div>
<div class="field" data-readonly="true">
<label for="textarea-readonly-en">Approved release note</label>
<textarea
id="textarea-readonly-en"
class="textarea"
name="approvedReleaseNote"
rows="4"
aria-describedby="textarea-readonly-reason-en"
defaultValue="Verification passed. Restore the previous package and rerun the smoke suite if health checks fail."
readonly
/>
<p id="textarea-readonly-reason-en" data-field-description>
Only the release owner can edit it; the value remains selectable and is
included in form submission.
</p>
</div>
</form>
Use the document or Field direction for authored prose. A nested technical token may set its own direction when punctuation order must remain left-to-right.
RTL release note
اذكر ما تغيّر وكيف تم التحقق وما الذي يجب فعله عند الفشل.
<div class="field w-full" dir="rtl">
<label for="textarea-rtl-en">ملاحظات الإصدار</label>
<textarea
id="textarea-rtl-en"
class="textarea"
rows="5"
aria-describedby="textarea-rtl-help-en"
placeholder="اكتب نتيجة التحقق وخطوات التراجع"
></textarea>
<p id="textarea-rtl-help-en" data-field-description>
اذكر ما تغيّر وكيف تم التحقق وما الذي يجب فعله عند الفشل.
</p>
</div>
Keep one persistent visible label. Placeholder text may demonstrate content but never supplies the only accessible name.
Keep the stable length policy in persistent guidance and associate it through aria-describedby. Give a visible live count its own localized label, keep it out of the description relationship, and avoid announcing every keystroke. Add the current error ID only after validation, then remove it after recovery.
Preserve native newline editing, selection, undo, paste, spellcheck, dictation, drag-selection, and IME behavior.
Keep the viewport bounded and vertically resizable so long input does not move the page while users edit near the caret.
Use one continuous inset focus boundary. Forced-colors mode replaces it with a system Highlight outline.
On coarse pointers, editable text remains at least 16px to avoid mobile focus zoom.
Test real mobile keyboards, physical CJK input methods, 200% zoom, touch selection, Windows forced colors, and screen-reader/browser combinations before release.