v0.5

Textarea

Multi-line text input for longer prose, with built-in size, state, and resize controls.

Description

Textarea is a multi-line text input rendered as a native <textarea>. It is a single-element component built around the same size and state model as the rest of the form primitives: xs, sm, md sizes, plus first-class loading, disabled, and invalid states surfaced through aria-invalid and a visual icon overlay.

Reach for it whenever a user needs to write more than one line: profile bios, message bodies, feedback, ticket descriptions, comments, prompts. Drop it inside Field to get a label, description, and helper text for free. Textarea reads size, loading, disabled, and invalid from Field via context, so a single <Field size="md"> cascades to its children without prop drilling.

Don't use Textarea for single-line input; Input is the right primitive there. For formatted or rich content (links, mentions, lists), use a dedicated rich-text editor instead. For structured key-value entries, compose multiple Field + Input rows rather than a single freeform textarea.

Installation

pnpm dlx @create-ui/cli add textarea

Usage

import { Textarea } from "@/components/ui/textarea"
<Textarea placeholder="Write something..." />

Examples

Sizes

Three sizes (xs, sm, md) adjust padding, radius, and type scale. sm is the default. When nested inside a Field, the textarea inherits size from the field; an explicit prop on the textarea always wins.

With Label

Wrap the textarea in Field and pair it with FieldLabel to get an accessible label. Use LabelRequired (or LabelOptional) for the required/optional indicator.

With Description

Add FieldDescription under the textarea for short, secondary copy explaining what the field is for. The description is rendered with reduced emphasis and linked to the textarea for assistive tech.

Your feedback helps us improve the product.

With Helper

Combine FieldHelper with LabelCount and maxLength to surface a live character counter alongside hint text. FieldHelper accepts an icon for inline status affordances.

0/300
Helper hint text for you.

Invalid

Set aria-invalid="true" on the textarea (or invalid on the parent Field, which cascades) to switch the textarea into the error state. The component renders a warning icon inside the textarea and re-tints the border, outline, text, and placeholder with the error tokens.

Message must be at least 20 characters.

Loading

loading on the parent Field (or directly on the textarea) shows an animated spinner inside the textarea and makes it non-interactive. The textarea is also visually disabled and switches to the info-tinted border.

Helper hint text for you.

Disabled

disabled cascades from Field disabled and removes the textarea from the tab order. The component drops to the disabled tokens for background, text, and placeholder.

Helper hint text for you.

Resizable

resizable="x" | "y" | "both" re-enables the native resize handle and renders a custom corner glyph in its place. The native browser handle is hidden so the affordance matches the rest of the design system.

You can resize this textarea vertically.

Accessibility

Textarea is a native <textarea>, so all of the platform's text-editing affordances apply. Keyboard shortcuts beyond the basics are handled by the browser.

KeyDescription
TabMoves focus into the textarea.
Shift + TabMoves focus to the previous element.
EnterInserts a newline.
Arrow keysMoves the caret one character or line.
Home / EndMoves the caret to the start or end of the current line.
Shift + ArrowExtends the selection one character or line.
Cmd/Ctrl + ASelects all text.

ARIA notes:

  • Uses native <textarea> semantics; no role is set.
  • aria-invalid is forwarded to the underlying element and is automatically derived from Field's invalid prop, so wrapping the textarea in <Field invalid> is enough to expose the error to assistive tech.
  • disabled removes the textarea from the tab order; loading also marks it as disabled to block input while a save is in flight.
  • The status icons (loading spinner, invalid warning) are aria-hidden because they duplicate state already exposed through aria-invalid / disabled. Communicate the underlying reason through FieldHelper, FieldDescription, or an aria-live region.

Styling

Tailwind override: pass className to merge Tailwind classes with the component's CVA classes (via cn()):

<Textarea className="min-h-[200px]" />

Data slots and attributes: the component sets these for CSS targeting:

  • data-slot="textarea" on the underlying <textarea>.
  • data-slot="textarea-wrapper" on the wrapper element (only rendered when resizable is set or a status icon is visible).
  • data-slot="textarea-status-icon" on the loading / invalid icon overlay.
  • data-size="<size>" on the textarea.
  • data-loading (presence-only) on the textarea while loading.
  • Standard aria-invalid="true" and disabled attributes for error and disabled states; the component targets these directly rather than mirroring them as data-*.

Target a specific state in CSS:

[data-slot="textarea"][aria-invalid="true"] {
  /* … */
}
  • Input: single-line text input; same Field cascade and state model.
  • Field: wraps form primitives to provide label, description, helper, and cascading size / disabled / loading / invalid.
  • Label: primitives (LabelRequired, LabelOptional, LabelCount, LabelBlock) used inside FieldLabel.

API Reference

Textarea

Multi-line text input that integrates with Field. Extends React.ComponentProps<"textarea"> with the native size attribute omitted so the design-system size axis can use it. Any other standard textarea attribute (name, value, defaultValue, placeholder, maxLength, onChange, aria-*, etc.) is accepted.

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md""sm"Size scale. Cascades from a parent Field if unset; an explicit prop always wins.
resizable"x" | "y" | "both"-Enables the native resize handle on the given axis and renders a custom corner glyph. Omit to keep the textarea fixed-size.
loadingbooleanfalseShows an animated spinner inside the textarea and disables input. Cascades from Field loading.
disabledbooleanfalseStandard disabled flag; also cascades from Field disabled.
aria-invalidboolean | "true" | "false"-Marks the textarea as invalid and shows the warning icon. Auto-derived from Field invalid when unset.
classNamestring-Tailwind classes merged with the component's CVA classes via cn().
childrenReact.ReactNode-Rarely used; textarea content is controlled via value / defaultValue, not children.

Variants

VariantOptionsDefaultDescription
size"xs" "sm" "md""sm"Size scale; controls padding, radius, type scale, and icon sizing.