Form controls

TextArea
v1.1.4
Documentation Under Review

A multi-line text input component for capturing user feedback, comments, and longer text entries.

Prefer AI-assisted development?

This documentation is available via the Catalyst MCP Server. Learn how to connect →

Catalyst TextArea provides an accessible multi-line text input with built-in size variants, danger and disabled states, focus ring styling, and character counter support — all from a single composable component.

Preview

Installation

npm install @pmi/catalyst-text-area
import { TextArea } from '@pmi/catalyst-text-area';

Variants

Danger

Use the danger prop to indicate a validation error or warning state. Pair with aria-invalid and an error message linked via aria-describedby.

Preview

Disabled

Use the disabled prop to prevent interaction. The opacity reduces to 40% to communicate the inactive state.

Preview

Sizes

The textarea supports three sizes: sm, md (default), and lg. Each step adjusts padding and text size.

Preview

Common Patterns

Precomposed

A complete field layout combining Label, TextArea, and character counter. The danger state activates automatically when the character limit is reached.

Preview
Maximum 100 characters.

With HelperText

Attach helper text or an error message below the textarea using the HelperText component with aria-describedby.

Preview
Maximum 100 characters.
Subtext

With Tags

Use the asChild prop to wrap a <div> container holding Tag chips and a plain <textarea> for free-form input. The inner <textarea> carries its own aria-label so screen readers can identify the input within the tag group.

Preview
Tag
Tag

API Reference

Prop

Type

The component also accepts all standard HTML textarea element attributes and forwards ref to the underlying element.

Accessibility

TextArea is designed to meet WCAG 2.2 AA standards.

ARIA attributes

  • Always associate the textarea with a visible <Label> using htmlFor / id.
  • Set aria-describedby to the id of the helper text or error HelperText. Render HelperText unconditionally (even when empty) so the referenced element is always in the DOM — a broken aria-describedby reference degrades the AT experience.
  • Set aria-invalid="true" when danger is active so assistive technologies communicate the error state.
  • Apply role="alert" to the HelperText conditionally (only when an error exists). Setting it unconditionally causes screen readers to announce the placeholder text as an error on page load.
  • Do not combine aria-live="polite" with role="alert" on the same element — role="alert" already implies aria-live="assertive". Adding aria-live="polite" creates a conflict with undefined behavior across AT.
  • Do not put aria-live on the <TextArea> / <textarea> itself — live region semantics are ignored on interactive widgets. Place the live region on a non-interactive container.

Character counter

Wrap the character counter in an element with role="status" so screen readers announce updates politely without interrupting the user. role="status" carries implicit aria-live="polite" and aria-atomic="true". Avoid rendering the counter as a <label> element without htmlFor — it becomes a stray unassociated label in the accessibility tree.

<span role="status" className="tabular-nums">
  {value.length}/{limit}
</span>

Keyboard Interactions

The following keys are supported when the textarea has focus. All behavior is native to the browser <textarea> element — Catalyst adds no custom key handling.

KeyBehavior
TabMoves focus into the textarea
Shift + TabMoves focus out of the textarea
↑ ↓ ← →Move the caret within the text
Home / EndMove the caret to the start or end of the current line
Standard input keysNative <textarea> behavior — type, delete, select, copy/paste

Choosing the Right Component

Use TextArea when the input is expected to span multiple lines. Reach for a different primitive when:

If you need…Use instead
A single-line text inputTextField
A styled label for a form controlLabel
Helper or error text below a controlHelperText

On this page