Textarea
tngTextarea is the multiline primitive for native <textarea> elements. It layers rows and resize behavior on top of the same headless input contract used by tngInput.
Use direct primitive attachment for a simple multiline field, or add tngInputGroup when the textarea needs shared shell chrome and trailing helper content.
What you get
- Real native
<textarea>semantics for editing, selection, and IME input. rowsnormalization and a stabledata-resizehook for styling.- The same grouped composition model as headless input via
tngInputGroup. - State hooks and ARIA pass-through without taking ownership of your visual design.
Simple examples
Compare the same headless textarea structure across plain CSS and Tailwind CSS styles.
Headless textarea (Plain CSS)
Headless textarea (Tailwind CSS)
Installation
Import the multiline primitive and optional group primitives only when you need them.
Primitives import
import { TngTextarea, TngInputGroup, TngSuffix } from '@tailng-ui/primitives';
Basic usage
1) Direct primitive attachment
Direct tngTextarea usage
<textarea tngTextarea rows="4" placeholder="Write release notes"></textarea>
2) Grouped textarea with shared shell
Add tngInputGroup when the textarea needs shared chrome, a helper suffix, or future adornments that should live inside the same container.
Grouped textarea composition
<div tngInputGroup class="release-notes-shell">
<textarea tngTextarea rows="5" placeholder="Summarize the rollout"></textarea>
<span tngSuffix aria-hidden="true">Autosaves</span>
</div>
Structure
The primitive never replaces the native element. You apply tngTextarea to a real <textarea>, and optionally wrap it with tngInputGroup to get leading/control/trailing regions and mirrored focus/invalid state hooks.
This lets multiline controls participate in the same slot-driven styling contract as other headless form primitives without coupling the DOM to a specific CSS framework.
Rows and resize
rowsis normalized to at least1.resizesupportsvertical,horizontal,both, andnone.- The primitive emits
data-resizeso design tokens can branch on the chosen resize mode.
Accessibility guidance
- Use a real label or ARIA name just as you would for any other native textarea.
- Decorative trailing status text should stay
aria-hidden="true". - Prefer helper text outside the group shell for longer validation or instructional copy.
Validation patterns
Recommended for unit tests (jsdom)
Stable invalid test textarea
<textarea tngTextarea rows="4" aria-invalid="true"></textarea>
Native browser validation path
Native required validation
<textarea tngTextarea rows="4" required></textarea>
Examples
Release notes composer
Release notes + autosave meta
<div tngInputGroup class="release-notes-shell">
<textarea
tngTextarea
rows="5"
aria-label="Release notes"
[value]="releaseNotes()"
(input)="onReleaseNotesInput($event)"
></textarea>
<span tngSuffix aria-hidden="true">Autosaves</span>
</div>
Readonly review state
Readonly textarea
<textarea
tngTextarea
rows="4"
readonly
aria-label="Readonly incident summary"
>Incident summary is locked while the review is pending.</textarea>
Common pitfalls
Correct
Textarea registered with the primitive
<div tngInputGroup>
<textarea tngTextarea rows="4"></textarea>
</div>
Incorrect
Missing tngTextarea
<div tngInputGroup>
<textarea rows="4"></textarea>
</div>