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.jsonpnpm dlx shadcn@latest add https://oui.open.gov.sg/r/text-area-field.jsonnpx shadcn@latest add https://oui.open.gov.sg/r/text-area-field.jsonbunx --bun shadcn@latest add https://oui.open.gov.sg/r/text-area-field.jsonThe 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, ormaxLengthfor native HTML validation. Errors display after the user commits a value or submits the form. - Custom validation: Use the
validateprop to provide a function that returns an error message string, ornull/trueif valid. - Realtime validation: Use the
isInvalidprop witherrorMessagefor immediate feedback as the user types. - Validation behavior: The
validationBehaviorprop 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-labelledbyandaria-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-labeloraria-labelledbyprop 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
| Prop | Type | Default | Description |
|---|---|---|---|
label | React.ReactNode | - | The label for the text area field |
description | React.ReactNode | - | The description text shown below the field |
errorMessage | React.ReactNode | ((validation: ValidationResult) => React.ReactNode) | - | The error message to display when validation fails |
value | string | - | The current value (controlled) |
defaultValue | string | - | 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 |
isDisabled | boolean | false | Whether the field is disabled |
isReadOnly | boolean | false | Whether the field is read-only |
isRequired | boolean | false | Whether the field is required |
isInvalid | boolean | false | Whether the field should display as invalid |
minLength | number | - | The minimum number of characters required |
maxLength | number | - | The maximum number of characters allowed |
validate | (value: string) => ValidationError | true | null | undefined | - | Custom validation function |
autoComplete | string | - | Hints for browser autocomplete |
name | string | - | 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 |
inputProps | Partial<TextAreaProps> | - | Additional props to pass to the textarea element |
classNames | SlotsToClasses<"base" | "label" | "input" | "description" | "error"> | - | Custom CSS classes for component slots |