Open UI

TextAreaField

A multi-line text field that allows a user to enter a plain text value with a keyboard.

import { TextAreaField } from "@opengovsg/oui"export const Example = () => {  return (    <TextAreaField      label="Comments about OUI"      inputProps={{        placeholder: "I love OUI because...",      }}    />  )}

Usage

Use TextAreaField for multi-line text (long-form input, comments, descriptions). Use TextField for single-line.

import { TextAreaField } from "@opengovsg/oui"
<TextAreaField label="Comments" />

Alternatively, install the component as local source via the shadcn CLI:

npx shadcn@latest add https://oui.open.gov.sg/r/text-area-field.json
pnpm dlx shadcn@latest add https://oui.open.gov.sg/r/text-area-field.json
npx shadcn@latest add https://oui.open.gov.sg/r/text-area-field.json
bunx --bun shadcn@latest add https://oui.open.gov.sg/r/text-area-field.json

The TextAreaField component provides a multi-line text input with built-in label, description, and error message support. It is built on React Aria's TextField and renders a <textarea> element instead of an <input>.

If the component does not have a visible label (by passing a label prop), an aria-label or aria-labelledby prop must be passed instead to identify it to assistive technology.

Examples

With Label and Description

Provide clear instructions to users with labels and descriptions.

import { TextAreaField } from "@opengovsg/oui"export const Example = () => {  return (    <TextAreaField      label="Feedback"      description="Max 500 characters."      inputProps={{        placeholder: "Tell us what you think",      }}    />  )}

With Error Message

Combine isInvalid and errorMessage props to show validation errors.

import { TextAreaField } from "@opengovsg/oui"export const Example = () => {  return (    <TextAreaField      label="Feedback"      isInvalid      defaultValue="x"      errorMessage="Please enter at least 10 characters."    />  )}

Sizes

Use the size prop to change the size of the text area field. Defaults to "md".

import { TextAreaField } from "@opengovsg/oui"export const Example = () => {  return (    <div className="flex flex-col gap-6">      <TextAreaField        size="xs"        label="Extra Small"        inputProps={{ placeholder: "Extra small size" }}      />      <TextAreaField        size="sm"        label="Small"        inputProps={{ placeholder: "Small size" }}      />      <TextAreaField        size="md"        label="Medium (default)"        inputProps={{ placeholder: "Medium size" }}      />    </div>  )}

Controlled

Use the value and onChange props to control the text area value programmatically.

import { useState } from "react"import { TextAreaField } from "@opengovsg/oui"export const Example = () => {  const [value, setValue] = useState("")  return (    <div className="flex flex-col gap-2">      <TextAreaField        label="Bio"        value={value}        onChange={setValue}        inputProps={{ placeholder: "Tell us about yourself" }}      />      <p className="text-base-content-medium text-sm">        {value.length} characters      </p>    </div>  )}

Disabled

Use the isDisabled prop to disable the text area field.

import { TextAreaField } from "@opengovsg/oui"export const Example = () => {  return (    <TextAreaField      label="Feedback"      defaultValue="This field is disabled."      isDisabled    />  )}

Read Only

Use the isReadOnly prop to make the text area field read-only. The value can be selected but not modified.

import { TextAreaField } from "@opengovsg/oui"export const Example = () => {  return (    <TextAreaField      label="Feedback"      defaultValue="This field is read-only."      isReadOnly    />  )}

Validation

See the Forms guide for end-to-end patterns including React Hook Form + Zod.

TextAreaField supports React Aria's form validation system:

  • Built-in constraints: Use isRequired, minLength, or maxLength for native HTML validation. Errors display after the user commits a value or submits the form.
  • Custom validation: Use the validate prop to provide a function that returns an error message string, or null/true if valid.
  • Realtime validation: Use the isInvalid prop with errorMessage for immediate feedback as the user types.
  • Validation behavior: The validationBehavior prop controls enforcement — "native" (default) prevents form submission when invalid, while "aria" only marks the field via ARIA attributes and allows submission.

Events

TextAreaField supports the following event handlers, inherited from React Aria's TextField:

  • onFocus / onBlur — Called when the textarea gains or loses focus.
  • onFocusChange — Called when the focus state changes, receiving a boolean.
  • onChange — Called when the text value changes, receiving the new string value.
  • onCopy / onCut / onPaste — Called on clipboard operations.
  • onCompositionStart / onCompositionEnd — Called during IME composition for internationalized text input.
  • onKeyDown / onKeyUp — Called on keyboard events.
  • onSelect — Called when text is selected within the textarea.

Accessibility

  • Built with a native <textarea> element for full browser and assistive technology support.
  • Label, description, and error message are automatically linked to the textarea via aria-labelledby and aria-describedby.
  • Required and invalid states are exposed to assistive technology.
  • Supports keyboard interaction: text entry, clipboard shortcuts, and standard textarea navigation.
  • If no visible label is provided, an aria-label or aria-labelledby prop must be set.

Slots

  • base: The root container wrapper.
  • label: The label text element.
  • input: The textarea element.
  • description: The description text below the field.
  • error: The error message text.

Custom Styles

You can customize the TextAreaField component by passing custom Tailwind CSS classes to the component slots via the classNames prop.

import { TextAreaField } from "@opengovsg/oui"export const Example = () => {  return (    <TextAreaField      label="Your feedback"      description="Tell us what you think."      classNames={{        base: "rounded-xl border-2 border-dashed border-amber-300 bg-amber-50 p-4",        label: "font-mono uppercase text-amber-900",        input:          "rounded-xl border-amber-300 bg-white font-mono text-amber-900 placeholder:text-amber-400",        description: "italic text-amber-600",      }}      inputProps={{ placeholder: "Type your feedback here..." }}    />  )}

Props

TextAreaField

PropTypeDefaultDescription
labelReact.ReactNode-The label for the text area field
descriptionReact.ReactNode-The description text shown below the field
errorMessageReact.ReactNode | ((validation: ValidationResult) => React.ReactNode)-The error message to display when validation fails
valuestring-The current value (controlled)
defaultValuestring-The default value (uncontrolled)
onChange(value: string) => void-Callback fired when the value changes
size"xs" | "sm" | "md""md"The size of the component
variant"outline" | "unstyled""outline"The visual variant of the textarea
isDisabledbooleanfalseWhether the field is disabled
isReadOnlybooleanfalseWhether the field is read-only
isRequiredbooleanfalseWhether the field is required
isInvalidbooleanfalseWhether the field should display as invalid
minLengthnumber-The minimum number of characters required
maxLengthnumber-The maximum number of characters allowed
validate(value: string) => ValidationError | true | null | undefined-Custom validation function
autoCompletestring-Hints for browser autocomplete
namestring-The name of the input, used when submitting a form
validationBehavior"native" | "aria""native"Whether to use native HTML form validation or ARIA-based validation
onFocus(e: FocusEvent) => void-Called when the textarea receives focus
onBlur(e: FocusEvent) => void-Called when the textarea loses focus
onFocusChange(isFocused: boolean) => void-Called when the focus state changes
inputPropsPartial<TextAreaProps>-Additional props to pass to the textarea element
classNamesSlotsToClasses<"base" | "label" | "input" | "description" | "error">-Custom CSS classes for component slots

On this page