Thumbprint logo

Components

Text Area

Multiline inputs for text

TextAreaV2 is built on React Aria and styled with Thumbprint v2 semantic tokens. Like v1 it is a controlled component: the visible text always matches the value prop, and onChange hands back the new value so you can store it in state.

The field also renders its own label and helper text. Prefer the label, description, and errorMessage props over composing Label and FormNote siblings: React Aria generates the ids and wires up the label association and aria-describedby, so the field is accessible with no ids to keep unique and no way for the error styling and the error text to fall out of sync.

Label

The label prop renders the label above the textarea and associates the two for assistive technologies. Every field needs a label; when the design has no room for a visible one, pass accessibilityLabel instead.

function TextAreaExample() {
    const [value, setValue] = React.useState('');

    return (
        <TextAreaV2
            label="Project details"
            value={value}
            placeholder="Tell us about your project"
            onChange={setValue}
        />
    );
}

Description

The description prop renders helper text below the textarea and points the textarea’s aria-describedby at it, so screen readers announce it along with the label.

function TextAreaExample() {
    const [value, setValue] = React.useState('');

    return (
        <TextAreaV2
            label="Project details"
            description="The more you share, the more accurate your quotes will be."
            value={value}
            placeholder="Tell us about your project"
            onChange={setValue}
        />
    );
}

Error message

The errorMessage prop puts the textarea in the error state — there is no separate hasError to remember — and renders the message below it. It replaces the description while the error is showing, so you can pass both and let the field swap them.

The error state is visual only, as it was in v1: it sets aria-invalid but leaves native validity alone, so a <form> holding an errored textarea still submits and your own validation stays in charge. isRequired is unaffected — it still applies the native required attribute, which the browser does enforce.

function TextAreaExample() {
    const [value, setValue] = React.useState('Help');
    const errorMessage =
        value.length < 20 ? 'Please describe your project in at least 20 characters.' : undefined;

    return (
        <TextAreaV2
            label="Project details"
            description="The more you share, the more accurate your quotes will be."
            errorMessage={errorMessage}
            value={value}
            onChange={setValue}
        />
    );
}

Error styling without a message

hasError turns the label, text, and border red without rendering a note. Use it only when the message lives elsewhere — such as a summary at the top of the form — since an error state with no explanation leaves users stuck.

function TextAreaExample() {
    const [value, setValue] = React.useState('Help');

    return <TextAreaV2 label="Project details" hasError value={value} onChange={setValue} />;
}

Disabled textarea

The isDisabled prop disables the textarea visually and functionally, and dims the built-in label along with it.

function TextAreaExample() {
    const [value, setValue] = React.useState('Repaint two bedrooms and the upstairs hallway.');

    return <TextAreaV2 label="Project details" isDisabled value={value} onChange={setValue} />;
}

Read-only textarea

The isReadOnly prop adds the readonly attribute, letting users focus and select the text without editing it. There is no separate read-only look, and read-only textareas are still submitted with the form, so an errorMessage on one still shows. A disabled textarea is the opposite: it keeps the disabled treatment and its error is not shown, since the browser skips disabled fields when submitting.

function TextAreaExample() {
    const [value, setValue] = React.useState('Repaint two bedrooms and the upstairs hallway.');

    return <TextAreaV2 label="Project details" isReadOnly value={value} onChange={setValue} />;
}

Limiting length

The maxLength prop caps the number of characters the browser accepts; onChange stops firing once the limit is reached. Say what the limit is in the description so it isn’t a surprise.

function TextAreaExample() {
    const [value, setValue] = React.useState('');

    return (
        <TextAreaV2
            label="Project details"
            description="Up to 60 characters."
            maxLength={60}
            value={value}
            placeholder="Tell us about your project"
            onChange={setValue}
        />
    );
}

Using an external Label and FormNote

For call sites that have not moved to the props yet, a sibling Label and FormNote still work: the id prop lands on the <textarea> element, so Label’s for associates the two natively. You are then responsible for keeping the ids unique and, in the error state, for keeping the hasError flags on all three components in sync — which is why the label, description, and errorMessage props are the better choice for new code.

In development, React Aria logs a console warning for this pattern because it can’t detect the external for/id association. The association is real and accessible, so the warning can be ignored; passing label or accessibilityLabel silences it.

function TextAreaExample() {
    const [value, setValue] = React.useState('');

    return (
        <div>
            <Label for="example-text-area-v2">Project details</Label>
            <TextAreaV2
                id="example-text-area-v2"
                value={value}
                placeholder="Tell us about your project"
                onChange={setValue}
            />
            <div className="mt1">
                <FormNote>The more you share, the more accurate your quotes will be.</FormNote>
            </div>
        </div>
    );
}

Props

TextAreaV2

  • value
    required

    The current value of the textarea.

    Type
    string
  • onChange
    required

    The function that is called when the textarea value changes.

    It receives two arguments: onChange(newValue, event).

    The consumer of this component should use that data to update the value prop passed in to this component.

    Type
    (newValue: string, event: React.ChangeEvent<HTMLTextAreaElement>) => void
  • id

    Adds a HTML id attribute to the textarea. This is used for linking the HTML with an external Label, matching the v1 TextArea pattern. Note: when neither label nor accessibilityLabel is provided, react-aria logs a development-only console warning it cannot detect the native for/id association. The association is real and accessible; the warning can be ignored (or avoided by using the label prop).

    Type
    string
  • label

    Text that appears above the textarea, replacing the need for a separate Label component. The label is automatically associated with the textarea for assistive technologies.

    Prefer this over accessibilityLabel: a visible label serves everyone, not only screen reader users, and clicking it focuses the textarea. Use accessibilityLabel only when the design leaves no room for visible text.

    Type
    string
  • description

    Text that appears below the textarea, replacing the need for a separate FormNote component. When the textarea is in an error state with an errorMessage, the error message is shown in its place.

    Type
    React.ReactNode
  • errorMessage

    Error text that appears below the textarea, replacing the need for a separate FormNote component. Providing an errorMessage puts the textarea in the error state — no separate hasError needed.

    Type
    React.ReactNode
  • isDisabled

    Visually and functionally disable the textarea.

    Type
    boolean
    Default
    false
  • isReadOnly

    Adds readonly HTML attribute, allowing users to select (but not modify) the textarea.

    Type
    boolean
    Default
    false
  • isRequired

    Adds the required HTML attribute to the textarea.

    Type
    boolean
    Default
    false
  • hasError

    Makes the textarea border and text color red.

    Type
    boolean
    Default
    false
  • placeholder

    Text that appears within the textarea when there is no value.

    Type
    string
  • name

    Adds name HTML attribute to element, indicating the property name associated with the selected value.

    Type
    string
  • maxLength

    The maximum number of characters that a user can enter. onChange will not fire if a user enters a character that exceeds maxLength.

    Type
    number
  • onFocus

    Fires when the textarea receives focus.

    Type
    (event: React.FocusEvent<HTMLTextAreaElement>) => void
  • onBlur

    Fires when the textarea loses focus.

    Type
    (event: React.FocusEvent<HTMLTextAreaElement>) => void
  • onKeyDown

    Fires when a key is pressed down with the textarea focused.

    Type
    (event: React.KeyboardEvent<HTMLTextAreaElement>) => void
  • onKeyUp

    Fires when a key press is released with the textarea focused.

    Type
    (event: React.KeyboardEvent<HTMLTextAreaElement>) => void
  • dataTestId

    A selector hook into the React component for use in automated testing environments. It is applied internally to the <textarea /> element.

    Type
    string
  • accessibilityLabel

    Accessible label for the textarea, applied as the aria-label attribute. Only needed if there is no label prop or associated label element. If you see react-aria's development-only warning asking for aria-label or aria-labelledby, this prop — or label — is the fix.

    Type
    string