Text Area

The text area component provides users with a resizable input field for entering longer text content. It includes features like character counting, validation states, and optional helper text to guide users through data entry tasks.

import {TextArea} from "@qualcomm-ui/react/text-area"

Examples

Simple

The simple <TextArea> bundles all subcomponents together into a single component.

Optional hint
<TextArea
  className="w-72"
  hint="Optional hint"
  label="Label"
  placeholder="Placeholder text"
/>

Composite

Build with the composite API for granular control. This API requires you to provide each subcomponent, but gives you full control over the structure and layout.

0/50
Optional hint
<TextArea.Root className="w-72" maxLength={50}>
  <TextArea.Label>Label</TextArea.Label>
  <TextArea.Counter />
  <TextArea.Input placeholder="Placeholder text" />
  <TextArea.Hint>Optional hint</TextArea.Hint>
  <TextArea.ErrorText>Optional error text</TextArea.ErrorText>
</TextArea.Root>

Counter

Display a character count using the counter prop or by setting a maxLength.

By default, the counter displays:

  • count/max when maxLength is set
  • count when only counter is used

Use counterProps.display to customize the format with a function that receives the current count and optional max length.

0
0/10
0 of 10 characters
<TextArea className="w-72" counter label="Counter without maxLength" />
<TextArea
  className="w-72"
  label="Counter with maxLength"
  maxLength={10}
/>
<TextArea
  className="w-72"
  counterProps={{
    display: (count, max) =>
      max ? `${count} of ${max} characters` : `${count} characters`,
  }}
  label="Custom counter display"
  maxLength={10}
/>

Sizes

Customize size using the size prop. The default size is md.

<TextArea className="w-56" defaultValue="sm" size="sm" />
<TextArea className="w-60" defaultValue="md" />
<TextArea className="w-64" defaultValue="lg" size="lg" />

States

The following shows how the TextArea component appears in each interactive state.

Invalid
<TextArea disabled label="Disabled" placeholder="Disabled" />
<TextArea label="Read only" placeholder="Read only" readOnly />
<TextArea label="Required" placeholder="Required" required />
<TextArea
  errorText="Invalid"
  invalid
  label="Invalid"
  placeholder="Invalid"
/>

Controlled State

Set the initial value using the defaultValue prop, or use value and onValueChange to control the value manually. These props follow our controlled state pattern.

import {type ReactElement, useState} from "react"

import {Button} from "@qualcomm-ui/react/button"
import {TextArea} from "@qualcomm-ui/react/text-area"

export function TextAreaControlledStateDemo(): ReactElement {
  const [value, setValue] = useState<string>("Controlled value")

  return (
    <div className="flex items-end gap-4">
      <TextArea.Root
        className="w-72"
        onValueChange={(updatedValue) => {
          console.debug("Value changed:", updatedValue)
          setValue(updatedValue)
        }}
        value={value}
      >
        <TextArea.Label>Label</TextArea.Label>
        <TextArea.Input placeholder="Placeholder text" />
      </TextArea.Root>

      <Button emphasis="primary" onClick={() => setValue("")} variant="outline">
        Reset
      </Button>
    </div>
  )
}

Error Text and Indicator

Error messages are displayed using two props:

  • invalid
  • errorText (or the TextArea.ErrorText component when using the composite API)

The error text and indicator will only render when invalid is true.

You must enter at least 10 characters.
<TextArea
  className="w-72"
  errorText="You must enter at least 10 characters."
  invalid={value.length < 10}
  label="Label"
  onValueChange={setValue}
  placeholder="Enter at least 10 characters"
  required
  value={value}
/>

Forms

Choose the form library that fits your needs—we've built examples with React Hook Form and Tanstack Form to get you started.

React Hook Form

Use React Hook Form to handle the input state and validation. ArkType works great for schema validation if you need it.

0
Minimum 10 characters
import {arktypeResolver} from "@hookform/resolvers/arktype"
import {type} from "arktype"
import {Controller, useForm} from "react-hook-form"

import {Button} from "@qualcomm-ui/react/button"
import {TextArea} from "@qualcomm-ui/react/text-area"

interface FormData {
  feedback: string
}

const FormSchema = type({
  feedback: type("string>=10").configure({
    message: "Feedback must be at least 10 characters long",
  }),
})

export function TextAreaReactHookFormDemo() {
  const {control, handleSubmit} = useForm<FormData>({
    defaultValues: {feedback: ""},
    resolver: arktypeResolver(FormSchema),
  })

  return (
    <form
      className="mx-auto flex w-full max-w-sm flex-col gap-3"
      noValidate
      onSubmit={(e) => {
        void handleSubmit((data) => console.log(data))(e)
      }}
    >
      <Controller
        control={control}
        name="feedback"
        render={({field: {onChange, ...fieldProps}, fieldState: {error}}) => (
          <TextArea
            className="w-full"
            counter
            errorText={error?.message}
            hint="Minimum 10 characters"
            invalid={!!error}
            label="Feedback"
            onValueChange={onChange}
            placeholder="Tell us about your experience"
            required
            {...fieldProps}
          />
        )}
      />

      <div className="flex w-full justify-end">
        <Button emphasis="primary" type="submit" variant="fill">
          Send Feedback
        </Button>
      </div>
    </form>
  )
}

Tanstack Form

Tanstack Form handles validation with its built-in validators.

0
Minimum 10 characters
import {useForm} from "@tanstack/react-form"

import {Button} from "@qualcomm-ui/react/button"
import {TextArea} from "@qualcomm-ui/react/text-area"

interface FeedbackFormData {
  feedback: string
}

export function TextAreaTanstackFormDemo() {
  const form = useForm({
    defaultValues: {
      feedback: "",
    } satisfies FeedbackFormData,
    onSubmit: ({value}) => {
      // Handle successful submission
      console.log("Form submitted:", value)
    },
  })

  return (
    <form
      className="mx-auto flex w-full max-w-sm flex-col gap-3"
      onSubmit={(e) => {
        e.preventDefault()
        void form.handleSubmit()
      }}
    >
      <form.Field
        name="feedback"
        validators={{
          onChange: ({value}) => {
            if (!value || value.trim().length < 10) {
              return "Feedback must be at least 10 characters long"
            }
            return undefined
          },
        }}
      >
        {(field) => (
          <TextArea
            className="w-full"
            counter
            errorText={field.state.meta.errors?.[0]}
            hint="Minimum 10 characters"
            invalid={field.state.meta.errors.length > 0}
            label="Feedback"
            name={field.name}
            onBlur={field.handleBlur}
            onValueChange={field.handleChange}
            placeholder="Tell us about your experience"
            value={field.state.value}
          />
        )}
      </form.Field>

      <div className="flex w-full justify-end">
        <Button
          disabled={form.state.isSubmitting}
          emphasis="primary"
          type="submit"
          variant="fill"
        >
          Send Feedback
        </Button>
      </div>
    </form>
  )
}

Tanstack form also supports ArkType.

API

<TextArea />

The TextArea extends the TextArea.Root with the following props:

PropType
Controls whether to display the counter element.

-
true: always show the counter
-
false: never show the counter
-
undefined (default): only show the counter if maxLength is set
boolean
Props applied to the counter element.
{
display?: (
count: number,
maxLength?: number,
) => ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
Optional error that describes the element when invalid is true.
string
Props applied to the error text element.
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
Optional hint describing the element. This element is automatically associated with the component's textarea element for accessibility.
Props applied to the hint element.
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
Props applied to the textarea element.
any
Optional label describing the element. Recommended. This element is automatically associated with the component's input element for accessibility.
Props applied to the label element.
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
HTML placeholder attribute, passed to the underlying textarea element.
string
Type
boolean
Description
Controls whether to display the counter element.

-
true: always show the counter
-
false: never show the counter
-
undefined (default): only show the counter if maxLength is set
Type
{
display?: (
count: number,
maxLength?: number,
) => ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
Description
Props applied to the counter element.
Type
string
Description
Optional error that describes the element when invalid is true.
Type
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
Description
Props applied to the error text element.
Description
Optional hint describing the element. This element is automatically associated with the component's textarea element for accessibility.
Type
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
Description
Props applied to the hint element.
Type
any
Description
Props applied to the textarea element.
Description
Optional label describing the element. Recommended. This element is automatically associated with the component's input element for accessibility.
Type
{
children?: ReactNode
render?:
| Element
| ((
props: Props,
) => Element)
}
Description
Props applied to the label element.
Type
string
Description
HTML placeholder attribute, passed to the underlying textarea element.

Composite API

<TextArea.Root>

Groups all parts of the text-area. Renders a <div> element by default.
PropTypeDefault
React children prop.
The initial value of the input when rendered. Use when you don't need to control the value of the input.
string
The document's text/writing direction.
'ltr' | 'rtl'
'ltr'
Whether the input is disabled. When true, prevents user interaction and applies visual styling to indicate the disabled state.
boolean
The id of the form that the input belongs to.
string
A root node to correctly resolve document in custom environments. i.e., Iframes, Electron.
    () =>
    | Node
    | ShadowRoot
    | Document
    The ids of the elements that are associated with the input. These will be automatically generated if omitted.
    {
    counter: string
    errorText: string
    hint: string
    input: string
    label: string
    }
    Controls the visual error state of the input. When true, applies semantic error styling to indicate validation failure.
    boolean
    The maximum number of characters allowed in the textarea.
    number
    The name of the input field. Useful for form submission.
    string
    The callback invoked when the field is focused or blurred.
      (
      focused: boolean,
      ) => void
      The callback invoked when the value changes.
        (
        value: string,
        ) => void
        Whether the input is read-only. When true, prevents user interaction while keeping the input focusable and visible.
        boolean
        Allows you to replace the component's HTML element with a different tag or component. Learn more
        | ReactElement
        | ((
        props: object,
        ) => ReactElement)
        Whether the input is required. When true, the input must have a value for form validation to pass.
        boolean
        The size of the textarea and its elements. Governs properties like font size, and item padding.
        | 'sm'
        | 'md'
        | 'lg'
        'md'
        
        The controlled value of the input
        string
        Description
        React children prop.
        Type
        string
        Description
        The initial value of the input when rendered. Use when you don't need to control the value of the input.
        Type
        'ltr' | 'rtl'
        Description
        The document's text/writing direction.
        Type
        boolean
        Description
        Whether the input is disabled. When true, prevents user interaction and applies visual styling to indicate the disabled state.
        Type
        string
        Description
        The id of the form that the input belongs to.
        Type
        () =>
        | Node
        | ShadowRoot
        | Document
        Description
        A root node to correctly resolve document in custom environments. i.e., Iframes, Electron.
          Type
          {
          counter: string
          errorText: string
          hint: string
          input: string
          label: string
          }
          Description
          The ids of the elements that are associated with the input. These will be automatically generated if omitted.
          Type
          boolean
          Description
          Controls the visual error state of the input. When true, applies semantic error styling to indicate validation failure.
          Type
          number
          Description
          The maximum number of characters allowed in the textarea.
          Type
          string
          Description
          The name of the input field. Useful for form submission.
          Type
          (
          focused: boolean,
          ) => void
          Description
          The callback invoked when the field is focused or blurred.
            Type
            (
            value: string,
            ) => void
            Description
            The callback invoked when the value changes.
              Type
              boolean
              Description
              Whether the input is read-only. When true, prevents user interaction while keeping the input focusable and visible.
              Type
              | ReactElement
              | ((
              props: object,
              ) => ReactElement)
              Description
              Allows you to replace the component's HTML element with a different tag or component. Learn more
              Type
              boolean
              Description
              Whether the input is required. When true, the input must have a value for form validation to pass.
              Type
              | 'sm'
              | 'md'
              | 'lg'
              Description
              The size of the textarea and its elements. Governs properties like font size, and item padding.
              Type
              string
              Description
              The controlled value of the input

              <TextArea.Label>

              An accessible label that is automatically associated with the input. Renders a <label> element by default.
              PropType
              React children prop.
              Allows you to replace the component's HTML element with a different tag or component. Learn more
              | ReactElement
              | ((
              props: object,
              ) => ReactElement)
              Description
              React children prop.
              Type
              | ReactElement
              | ((
              props: object,
              ) => ReactElement)
              Description
              Allows you to replace the component's HTML element with a different tag or component. Learn more

              <TextArea.Hint>

              Helper text displayed below the textarea. Renders a <div> element by default.
              PropType
              React children prop.
              Allows you to replace the component's HTML element with a different tag or component. Learn more
              | ReactElement
              | ((
              props: object,
              ) => ReactElement)
              Description
              React children prop.
              Type
              | ReactElement
              | ((
              props: object,
              ) => ReactElement)
              Description
              Allows you to replace the component's HTML element with a different tag or component. Learn more

              <TextArea.Counter>

              Character counter displayed opposite the textarea label. Renders a <div> element by default.
              PropType
              Customize how the counter is displayed. Receives the current character count and optional max length, and returns a React node.
                (
                count: number,
                maxLength?: number,
                ) => ReactNode
                Allows you to replace the component's HTML element with a different tag or component. Learn more
                | ReactElement
                | ((
                props: object,
                ) => ReactElement)
                Type
                (
                count: number,
                maxLength?: number,
                ) => ReactNode
                Description
                Customize how the counter is displayed. Receives the current character count and optional max length, and returns a React node.
                  Type
                  | ReactElement
                  | ((
                  props: object,
                  ) => ReactElement)
                  Description
                  Allows you to replace the component's HTML element with a different tag or component. Learn more

                  <TextArea.Input>

                  The text area element. Renders a <textarea> element.

                  <TextArea.ErrorText>

                  Error message displayed when the textarea is invalid. Renders a <div> element by default.
                  PropType
                  React children prop.
                  Allows you to replace the component's HTML element with a different tag or component. Learn more
                  | ReactElement
                  | ((
                  props: object,
                  ) => ReactElement)
                  Description
                  React children prop.
                  Type
                  | ReactElement
                  | ((
                  props: object,
                  ) => ReactElement)
                  Description
                  Allows you to replace the component's HTML element with a different tag or component. Learn more