Textarea

A styled multi-line text input with smooth focus transitions and shadow effects.

Basic

With label

With character count

Up to 160 characters.

Error state

Feedback is required.

Disabled

Installation

pnpm
pnpm dlx @hex-core/cli add textarea

With label

Textarea paired with a label

tsx
<div className="grid gap-1.5">
  <Label htmlFor="message">Message</Label>
  <Textarea id="message" placeholder="Type your message..." />
</div>

With character counter

Controlled textarea with a live character count anchored beneath the field. Common for bio/description inputs with a hard cap.

tsx
import { useState } from "react";

const MAX_LENGTH = 280;

export function BioField() {
  const [value, setValue] = useState("");
  const remaining = MAX_LENGTH - value.length;
  const counterId = "bio-counter";

  return (
    <div className="grid gap-1.5">
      <Label htmlFor="bio">Bio</Label>
      <Textarea
        id="bio"
        rows={4}
        maxLength={MAX_LENGTH}
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder="Tell us a little about yourself"
        aria-describedby={counterId}
      />
      <p
        id={counterId}
        className={`text-xs tabular-nums ${remaining < 20 ? "text-destructive" : "text-muted-foreground"}`}
        aria-live="polite"
      >
        {value.length} / {MAX_LENGTH}
      </p>
    </div>
  );
}

API Reference

PropTypeDefaultDescription
placeholder
stringPlaceholder text
rows
number3Number of visible text rows
disabled
booleanfalseDisable the textarea
value
stringControlled textarea value
defaultValue
stringDefault value for uncontrolled usage
onChange
functionChange handler: (e: ChangeEvent<HTMLTextAreaElement>) => void
className
stringAdditional CSS classes

AI Guidance

When to use

Use for multi-line text input: comments, descriptions, messages, notes. Always pair with a Label.

When not to use

Don't use for single-line input (use Input). Don't use for rich text editing.

Common mistakes

  • Missing associated Label
  • Not setting a reasonable min-height or rows
  • Reaching for Textarea for short single-line input — Input is the right primitive when the content is ≤80 characters

Accessibility

Always pair with a Label using htmlFor/id. Consider aria-describedby for character limits.

Related components

Token budget: 250

Verified against @hex-core/components@1.12.0