Skip to content
HyperDesign System

Textarea

Multi-line text. The same outline, hover and focus ring as Input, with 12px corners, a 64px minimum and field-sizing: content, so it grows with what you type. Source: src/components/ui/textarea.tsx.

Default

Pair it with a Label. The minimum height is 64px, about three lines.

Auto-grow

Type into the first box: it grows a line at a time, then scrolls once it hits its max-h. Always cap it. The browser's resize handle still works; add resize-none to remove it.

content-sized · max-h-48
rows={4} · max-h-24

Variants

Default draws the box; bare removes it for a textarea that lives inside a surface you draw, such as a reply box.

variant="default"
variant="bare"

States

Same rules as Input: hover lifts the outline, focus rings it, disabled fades to 50% on a tint, aria-invalid goes destructive.

placeholder
readOnly
disabled
aria-invalid="true"

Validation and limits

No Field primitive exists, so the counter, message and aria wiring are composed by hand. Clear the box and tab away, or paste past 280 characters.

66/280

The box grows with the text, up to 192px, then scrolls.

Props

Textarea props
PropTypeDefaultDescription
variant"default" | "bare""default"bare strips the box and switches to field-sizing-fixed so a caller's auto-grow (the composer) controls height. Mirrored as data-variant.
aria-invalidboolean | "true" | "false"—Destructive outline and ring. Render and link the message yourself.
...propsComponentProps<"textarea">—Native attributes pass through (rows, maxLength, disabled, readOnly). className merges last; use it for max-h-* or resize-none.

Best practices

When to use

  • Text that may run past one line: descriptions, instructions, feedback.
  • A composer with attachments and a send button is Input group with InputGroupTextarea, not a bare Textarea.

Behavior

  • Let it grow, but always set a max-h so it scrolls instead of pushing the page.
  • Show a counter when there is a hard limit; prefer a soft limit with a message over maxLength silently eating input.
  • Enter makes a new line. If Enter should submit, say so and support Cmd/Ctrl+Enter.

Accessibility

  • Label it, and link the hint, counter or error with aria-describedby.
  • Use aria-live="polite" on the message so changes are announced without stealing focus.

Content

  • The placeholder shows an example of a good answer, not the label repeated.
  • Errors name the fix: Trim it to 280 characters.