Skip to content

Textarea

Stable

A multi-line text field that grows with its content — for messages, descriptions and feedback.

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/textarea.json

Usage

import { Field, FieldLabel } from "@/components/ui/field"
import { Textarea } from "@/components/ui/textarea"
<Field>
  <FieldLabel htmlFor="message">Message</FieldLabel>
  <Textarea id="message" placeholder="Tell us what happened…" />
</Field>

Textarea shares its border, fill, focus halo and invalid styling with Input, so the two sit together in any form without special casing.

Examples

Grows with content

Textarea uses field-sizing: content: it starts at a comfortable three lines and grows as people type, so nobody writes into a keyhole. Cap the growth with a max-h-* class — past it, the field scrolls. In browsers without field-sizing support it keeps its minimum height and can be resized by dragging.

Character limit

Show the limit before people hit it, count as they type, and switch to an error when they go over — rather than silently truncating their words. The counter is linked with aria-describedby; a separate polite live region speaks up only near the limit, so screen reader users aren't interrupted on every keystroke.

Disabled and read-only

Disabled textareas are skipped by focus and excluded from form data. Read-only ones stay focusable, selectable and scrollable — use them for text people need to read or copy, like an audit message.

In a comment form

A textarea often carries its own small set of actions. Keep the primary action last and the field wide enough for a real sentence.

GH

Guidelines

When to use

  • Open-ended answers longer than a line: feedback, descriptions, bios, messages.
  • Content where line breaks matter, such as release notes or addresses.

When not to use

  • Short answers — an Input signals "keep it brief" better.
  • Rich formatting such as bold text, lists or mentions — use a dedicated editor.
  • A chat composer with attachments and send buttons — compose an Input Group with InputGroupTextarea.

Size for the answer you expect

The starting height sets expectations. A one-line-tall textarea suggests a short answer; three to five lines invite a considered one. Don't disable resizing unless the layout genuinely can't accommodate it — people with larger text settings rely on it.

Do.A few lines of room invite a real answer; the limit is visible up front.
Don’t.A single-line box for an open question hides how much you want to hear.

Accessibility

Textarea is a native <textarea>: it announces as a multi-line edit field and supports every browser feature for free, including spellcheck, dictation and IME composition.

  • Label it. Associate a visible FieldLabel with htmlFor/id, or add an aria-label.
  • Describe limits. Put limits and formatting hints in a FieldDescription linked with aria-describedby, not in the placeholder.
  • Announce sparingly. Live-region counters that speak on every keystroke drown out what people are typing. Announce only when they approach or exceed the limit.
  • Contrast and size. The border meets 3:1 against the page, and text is 16px on small screens to prevent iOS zoom.
KeyBehavior
Tab
Moves focus into the textarea.
Enter
Inserts a line break. It never submits the form.
CtrlEnterEnter
A common convention for submitting from a textarea — wire it up yourself when it fits the flow.

API reference

Textarea

Renders a native <textarea> element and accepts all of its attributes.

PropTypeDefault
value

The controlled value. Pair with onChange.

stringNo default
defaultValue

The initial value of an uncontrolled textarea.

stringNo default
rows

Initial visible lines where field-sizing isn't supported. prfct sets a min-h-20 floor.

numberNo default
maxLength

Hard character limit enforced by the browser. Prefer a visible counter for soft limits.

numberNo default
disabled

Prevents interaction and excludes the value from form submission.

booleanfalse
readOnly

Prevents editing while keeping the text focusable, selectable and submitted.

booleanfalse
aria-invalid

Shows the invalid border and focus halo. Pair with a FieldError.

booleanNo default