Persona

Text Area

Inputs · 8 props · Updated Sep 22, 2026

Text Area renders one <textarea> and nothing else. The label above it, the helper text below it and the id that connects them all belong to FormField, which is why the anatomy below shows the two together — and why TextAreaField, which is the two of them already composed, is the component to render.

  • Up to 500 characters.
    Up to 500 characters.
    Label what is being asked. It belongs to FormField, which also ties it to the field for a screen reader.
  • Up to 500 characters.
    Up to 500 characters.
    Text area the field itself, and the whole of what TextArea renders. The corner handle is the browser’s, not a prop.
  • Up to 500 characters.
    Up to 500 characters.
    Helper text (optional) the rule or the example, also FormField’s. It is the only one of these that survives the first keystroke — a placeholder does not.

A five-line box is a request for five lines. It suits a description, a comment, a note, an explanation of something the form could not ask for directly. A one-word answer in one reads as a mistake the reader has made, so ask for that in a single-line input, where the size of the field matches the size of the answer.

The field accepts plain text, and a newline is the only formatting it keeps. PDS has no rich text editor, so anything needing bold or a list has to be built outside the system. Where the answer is one of a known set, it belongs in a Combobox or a Select rather than in prose someone has to read.

A textarea and its label ship as one component

Section titled “A textarea and its label ship as one component”

TextAreaField takes FormField’s props and the textarea’s props in one object and splits them internally, so label, helperText, errorText and warningText sit beside value, onChange and maxLength in a single call. It also settles the id: it hands FormField whatever inputId or id you passed, which is the one thing hand-composition gets wrong quietly, because a textarea given its own id inside a FormField ends up with a label pointing somewhere else.

Hand-composing a FormField around a textarea buys nothing the field component does not already do. The case for doing it anyway is on that page, and it is about what else sits in the input slot.

Change the height in CSS, not with the rows attribute

Section titled “Change the height in CSS, not with the rows attribute”

A text area is 128 pixels tall, from a theme token the component applies after the shared input styles. That beats the rows attribute, so passing rows changes nothing about the rendered field. Set the height in your own stylesheet.

The resize handle is the browser’s, and PDS does not remove it

Section titled “The resize handle is the browser’s, and PDS does not remove it”

resize is CSS, not a prop. The corner handle is the browser’s default for a <textarea>, and nothing in PDS removes it. Fix the height in your stylesheet when layout stability matters, and leave it resizable when it doesn’t. That decision lives in CSS, which is why you will not find a prop for it.

Constrain the resize where growth would break the layout or cover something beside the field, which a reader dragging the handle has no way to anticipate.

Do not reach for size to change the height

Section titled “Do not reach for size to change the height”

size sets the padding and the type scale, the same two things it sets on every input, and not the height. The height override lands after it, so sm and md are both 128 pixels tall.

A bare textarea reaches a screen reader unnamed

Section titled “A bare textarea reaches a screen reader unnamed”

Text Area has no label prop, and it reads its id from FormField’s context. That id is what the label’s for attribute points at, so a textarea rendered outside either wrapper is an unnamed text box. Passing aria-label through to the element names it without showing anything, which is the wrong trade for a field asking for a paragraph.

The error state does not travel with the error text

Section titled “The error state does not travel with the error text”

aria-invalid and the red border both come from Text Area’s own hasError, which it reads from its props and not from FormField’s context. TextAreaField does not derive one from the other either: it routes errorText to the label half and hasError to the textarea, so a field given only errorText prints a red message beside a box that still looks and announces as valid. Pass both.

Select and Combobox do read the error state from context, which is why this is worth checking on a textarea specifically and not on the others.

Nothing associates the message itself with the field. See FormField for what that costs and how to wire it.

Say the limit before the reader reaches it

Section titled “Say the limit before the reader reaches it”

maxLength is the element’s own attribute, so the browser enforces it in silence: keystrokes past the limit do nothing, with no message and nothing announced. Put the number in helper text, and add a counter when the limit is tight enough that someone will hit it mid-sentence.

Helper text is also the only text around the field that survives the first keystroke. Anything the reader still needs while typing goes there rather than in the placeholder. Writing has the rest of the rules for both.

TextAreaField is the larger API of the two, and it is every prop below plus the twelve FormField carries. Its table comes first.

NameType
label

ReactNode

Renders <Label> above input

inputId

string

ID of the input element. Providing this will automatically link the label to the input.

helperText

ReactNode

Renders <HelperText> below input

errorText

ReactNode

Renders <ErrorText> below input and helper text.

warningText

ReactNode

Renders <WarningText> below input and helper text.

labelProps

FormLabelProps

helperProps

(HelperTextProps & RefAttributes<HTMLDivElement>)

warningProps

(Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, “ref”> & RefAttributes<HTMLDivElement>)

errorProps

(Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, “ref”> & RefAttributes<HTMLDivElement>)

configField

boolean

Whether to render the form field as a config field. This will render the helper text as a tooltip.

configFieldInputWidth

string

Override the width of the input area when configField is true. Accepts any valid CSS width value (e.g. ‘33%’, ‘66.67%’). Defaults to ‘50%‘.

rightActions

ReactNode

Renders actions to the right of the label.

hasError

boolean

hasWarning

boolean

hovered

boolean

Visual state override. Forces the :hover styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

focused

boolean

Visual state override. Forces the :focus styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

active

boolean

Visual state override. Forces the :focus-within styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

checked

boolean

Visual state override. Forces the :checked styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

mixed

boolean

Visual state override. Forces the :indeterminate styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

size

InputSizes

Size of the input. If you need the HTML size attr, use inputSize.

Also accepts every prop of ComponentPropsWithoutRef<‘textarea’>.

Text Area’s own props, which TextAreaField also accepts. Eight of them, and five pin a visual state: hovered, focused, active, checked and mixed force a styling state on permanently, for specimens and visual tests rather than application code. checked and mixed arrive with the shared input type, and a multi-line field has neither state to pin.

What you will actually pass comes from the element: value, onChange, placeholder, maxLength, readOnly and the rest of a textarea’s own API.

NameType
hasError

boolean

hasWarning

boolean

hovered

boolean

Visual state override. Forces the :hover styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

focused

boolean

Visual state override. Forces the :focus styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

active

boolean

Visual state override. Forces the :focus-within styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

checked

boolean

Visual state override. Forces the :checked styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

mixed

boolean

Visual state override. Forces the :indeterminate styling on permanently, whatever the real state is. For specimens and visual tests, not application code.

size

InputSizes

Size of the input. If you need the HTML size attr, use inputSize.

Also accepts every prop of ComponentPropsWithoutRef<‘textarea’>.

Was this page helpful?