The box a person types several lines of words into, such as a note about what they saw on a trip, framed by the Field that gives it its label, its line of help and its error message.
When to use
Use TextArea for words a person types that run to sentences, such as a note about what they saw on a trip, with its label, its line of help and its error.
| Use instead | When |
|---|---|
| TextField | The words fit one line, such as a name or an email address |
| NumberField | The value is a number, typed or stepped up and down |
| SearchField | The words find a line or a stop |
| Select | The value is one of a short, fixed list |
Kongens Nytorv
Metro M1, M2 and M3.
Say where and when, such as the stop and the time
Variants and states
| Rest | Hover | Pressed | Focus | Disabled | |
|---|---|---|---|---|---|
| Default | Say where and when, such as the stop and the time | Say where and when, such as the stop and the time | Say where and when, such as the stop and the time | Say where and when, such as the stop and the time | Say where and when, such as the stop and the time |
- Rest
Say where and when, such as the stop and the time
- Hover
Say where and when, such as the stop and the time
- Pressed
Say where and when, such as the stop and the time
- Focus
Say where and when, such as the stop and the time
- Disabled
Say where and when, such as the stop and the time
Props
| Name | Type | Default | Description |
|---|---|---|---|
label* | ReactNode | — | Required. The field's name, in sentence case, such as "What happened": shown above the box, and the textarea's accessible name. |
hiddenLabel | boolean | false | Whether the label is heard but not seen. Use it only where the screen around the field already says what it is for. |
optional | boolean | false | Whether the person may leave the field empty. Adds "(optional)" after the label's words, seen and heard as part of the textarea's name. It sets no check: required does. |
optionalLabel | string | The locale's | Overrides "(optional)", the mark after the label's words with optional. |
description | ReactNode | — | A line of help between the label and the box, such as "Say where and when, such as the stop and the time", read after the label by a screen reader. Left out, no description is rendered. |
error | ReactNode | — | What is wrong with the words and how to put it right, such as "Say what happened". Given, it marks the field invalid and is shown under the box; left out, the field shows Base UI's own message when the textarea fails its checks. |
disabled | boolean | false | Whether the field ignores the person: the box takes no focus, no typing and no resizing, and the label, the help and the words turn to the tertiary ink. |
name | string | — | The name the words are submitted under in a form's FormData. |
rows | number | 3 | How many lines of words the box shows before they scroll. The person can drag the box taller. |
placeholder | string | — | Words shown in the empty box, such as "Such as the 5C waited at Nørreport for 10 minutes". They vanish as the person types, so they never replace the label or the description. |
required | boolean | false | Whether the field must hold words before a form is submitted: an empty box fails the browser's check, and Base UI shows the error. |
value | string | — | The words in the box, when the caller controls them. |
defaultValue | string | — | The words in the box at first, when the textarea keeps them itself. Left out, the box starts empty. |
onChange | (event: ChangeEvent<HTMLTextAreaElement, Element>) => void | — | React's own change event, called on every change to the words; event.target.value holds them, new lines included. Native, so a form library's register() or handleChange connects unchanged. |
ref | Ref<HTMLTextAreaElement> | — | Reaches the root, the <textarea>. |
slotProps | { field?: Omit<FieldRootProps, "children" | "disabled" | "name"> | undefined; label?: FieldLabelProps | undefined; description?: FieldDescriptionProps | undefined; error?: FieldErrorProps | undefined; } | — | Props for each part: field, label, description, error. |
label (required)ReactNode, default —- Required. The field's name, in sentence case, such as "What happened": shown above the box, and the textarea's accessible name.
hiddenLabelboolean, defaultfalse- Whether the label is heard but not seen. Use it only where the screen around the field already says what it is for.
optionalboolean, defaultfalse- Whether the person may leave the field empty. Adds "(optional)" after the label's words, seen and heard as part of the textarea's name. It sets no check:
requireddoes. optionalLabelstring, default The locale's- Overrides "(optional)", the mark after the label's words with
optional. descriptionReactNode, default —- A line of help between the label and the box, such as "Say where and when, such as the stop and the time", read after the label by a screen reader. Left out, no description is rendered.
errorReactNode, default —- What is wrong with the words and how to put it right, such as "Say what happened". Given, it marks the field invalid and is shown under the box; left out, the field shows Base UI's own message when the textarea fails its checks.
disabledboolean, defaultfalse- Whether the field ignores the person: the box takes no focus, no typing and no resizing, and the label, the help and the words turn to the tertiary ink.
namestring, default —- The name the words are submitted under in a form's
FormData. rowsnumber, default3- How many lines of words the box shows before they scroll. The person can drag the box taller.
placeholderstring, default —- Words shown in the empty box, such as "Such as the 5C waited at Nørreport for 10 minutes". They vanish as the person types, so they never replace the label or the description.
requiredboolean, defaultfalse- Whether the field must hold words before a form is submitted: an empty box fails the browser's check, and Base UI shows the error.
valuestring, default —- The words in the box, when the caller controls them.
defaultValuestring, default —- The words in the box at first, when the textarea keeps them itself. Left out, the box starts empty.
onChange(event: ChangeEvent<HTMLTextAreaElement, Element>) => void, default —- React's own change event, called on every change to the words;
event.target.valueholds them, new lines included. Native, so a form library'sregister()orhandleChangeconnects unchanged. refRef<HTMLTextAreaElement>, default —- Reaches the root, the
<textarea>. slotProps{ field?: Omit<FieldRootProps, "children" | "disabled" | "name"> | undefined; label?: FieldLabelProps | undefined; description?: FieldDescriptionProps | undefined; error?: FieldErrorProps | undefined; }, default —- Props for each part:
field,label,description,error.
Use it
import { TextArea } from '@mylder/perron'Ask your agent
get_reference({ library: "base-ui", component: "Field" })Keyboard and ARIA
From the Base UI reference for Field and the installed FieldControl. "From" says who does it. TextArea adds no keys.
| Key | Does | From |
|---|---|---|
| Typing | Changes the words and calls onChange(event); marks the field filled and dirty | Base UI |
Enter | Starts a new line; it never submits a form, and Base UI's Enter handling, which runs only on an <input>, does nothing here | Browser |
Tab, Shift+Tab | Move focus to and from the textarea, the field's one tab stop; leaving marks it touched, and with validationMode="onBlur" runs its checks | Base UI |
| A press on the label | Focuses the textarea: natively through htmlFor, or through Base UI's own handler for a hidden label | Base UI |
| The rest | Move the caret by character, word and line, select, cut, copy and paste, as the browser's own <textarea> does | Browser |
| Element | Role | Accessible name from | States announced |
|---|---|---|---|
root | textbox, native, multi-line: the browser exposes aria-multiline itself | aria-labelledby the label, and its native label through htmlFor: "What happened", or "What happened (optional)" with optional | multi-line; aria-describedby the description, then the mounted error; aria-invalid while invalid and not disabled; required; disabled; readonly |
field | none: a <div> | none | none |
label | none: a native <label>, or a hidden span | Its text: the caller's label, then "(optional)" with optional | none |
description | none: a <p> | none | Read as the textarea's description |
error | none: a <div> | none | Read as the textarea's description while it is mounted, with no live region of its own (Field's spec) |
What axe won't catch, from the contract, each with how this component meets it:
- Everything the eye is told, the ear is told, for every shape each prop accepts: the label names the textarea, even hidden, with the
optionalmark inside it; the description and the error are the textarea'saria-describedby; an invalid field isaria-invalidand its error is words beside the red edge, never the edge alone; the placeholder is never the name, and a story proves the textarea is named by the label with a placeholder given; the box being several lines is its native multi-line role, heard as such. - Every landmark and named region has an accessible name: none here; TextArea is not a landmark, and a group of fields is a Fieldset's to name.
- Every default label can be overridden:
optionalLabeloverrides "(optional)", the one owned string, Field's. - Every shape the type accepts renders, in every variant:
label,descriptionanderrorrender a string, an element and a list alike, as Field's do; a value with new lines keeps them; a long Danish compound wraps in the label, the description, the error and the box (CR-7). - A hidden copy kept for animation is
aria-hidden, and a collapsed region isinert: none here; the error is unmounted while the field is valid. - Native semantics first: a native
<textarea>with its ownrequired,readonly,minlengthandmaxlength, and a native<label>; Perron adds no ARIA, and Base UI onlyaria-labelledby,aria-describedbyandaria-invalid. - Focus moves in reading order and returns on close: the textarea is the field's one tab stop, between the label and description above it and the error below; Tab leaves it, since a textarea takes no Tab of its own; nothing opens or closes.
- Every target is at least
size.target-minsquare: the box, at leastsize.inputtall and as wide as its container; the label is a larger target for the same textarea. The resize grip is the browser's own and sized by it, which WCAG 2.2's target size (2.5.8) excepts as a user-agent control, and the box works without it, its words scrolling. - Loops stop when the system asks for less motion: none here; TextArea has no loop and no transition (CR-8).
Owned strings
| Key | English | Override | Seen or heard where |
|---|---|---|---|
optional | "(optional)" | optionalLabel | Field's optional span, seen after the label's words with optional, and heard in the textarea's name |
optional is the key packages/perron/src/locale/en.ts already has, Field's. TextArea reads no string itself: it passes optional and optionalLabel to Field, which reads it. Every other word is the caller's, through label, description, error and placeholder, or Base UI's message for a failing check: the caller's validate result, or the browser's own message, Field's known exception, which applies here unchanged (Field).
Do and don't
For a caller using the component, with the reason. Each row is a rule: a must row's Don't is never right, a should row's needs a reason, and Checked by says what catches the Don't in a caller's code.
| Rule | Level | Do | Don't | Checked by |
|---|---|---|---|---|
| DD-1 | should | Use TextArea for words that run to sentences, such as what the person saw on a trip | Use it for a name, an address or a number: TextField and NumberField take one line, its keys and its autofill | review |
| DD-2 | must | Give a label that names the words, "What happened", and hide it with hiddenLabel only where the screen already says it | Let the placeholder stand in for the label: it vanishes as the person types, and it is drawn in color.text.tertiary, below text contrast | review |
| DD-3 | must | Put help the person needs while typing in description, "Say where and when, such as the stop and the time" | Put that help in the placeholder: it is gone once they type | review |
| DD-4 | must | Word an error as what to do, "Say what happened" | Blame the person or say only that it failed ("Invalid input"), against Content design | review |
| DD-5 | should | Set rows to the length of words you expect, so most of them fit without scrolling | Set rows to fill the screen: a tall empty box asks for more than the form needs, and the person can drag it taller | review |
| DD-6 | should | Spread React Hook Form's register('note') on TextArea, or pass Formik's handleChange, as on a native <textarea> | Wrap TextArea in a controller to read its value: its onChange is the native one already | review |
| DD-7 | must | Give error from the form library whenever the field is invalid | Set slotProps.field.invalid with no message: the red edge alone tells only the eye, and only by its colour | review |
| DD-8 | should | Set optional on the few fields the person may leave empty, and required where an empty field must fail | Write "(optional)", "(required)" or an asterisk into label yourself: it misses the locale, and an asterisk needs a legend | review |
| DD-9 | must | Let Enter start a new line, and give the form a submit button | Make Enter submit the form from a TextArea: the person loses the one key that starts a new line | review |
| DD-10 | must | Use TextArea on its own: it already has its label, its help and its error | Wrap TextArea in Field: the textarea would be labelled twice, as Field's spec says | review |
| DD-11 | should | Set the width on the container, or on the frame through slotProps.field | Give the textarea a width through style or cols: the label and the error take the frame's width, not the box's | review |