Textarea
The input's multiline counterpart. It owns its label, its error message, and its busyness, and it grows with what you type.
className styles the wrapper
Textarea renders a wrapper around the <textarea> so it can hold the label and the error message. className styles that wrapper, not the field. Every other native prop passes through to the <textarea>. Stock shadcn puts className on the field itself, so a copied snippet lands somewhere else than you expect. Every example on this page sets w-72 on the wrapper.
Autosize
There is no size prop and no drag handle. One scale matches the input's default size so fields line up, and the height comes from the content instead: minRows (default 3) sets the empty height, maxRows (default 8) caps the growth, and the native scrollbar takes over past the cap. Growth is instant — a spring per keystroke would fight the caret.
Error
Pass error and the field owns the whole failure: the border, the ring, and the label turn destructive, and the message renders below. The field grows downward only, never sideways. There is no shake — the mood is soft, so errors arrive calmly.
Write at least ten characters
Write at least ten characters
Loading
loading pins the spinner to the top-right corner inside the padding. A textarea has no end slot — text flows across every line, so the spinner overlays the corner instead of sitting beside the content. The field reserves that column while it loads, so no line ever runs under the spinner. The field stays editable. Loading here means background work — an autosave, an async check — and the submit button is what locks a flow. With an error alongside it, both show and the spinner turns destructive too.
That draft failed to save
That draft failed to save
Disabled and read-only
Disabled dims the field and its label together and takes no pointer events. Read-only keeps full text contrast on a muted background, takes a normal cursor, and stays focusable so the value can still be read and copied. It keeps the focus ring: a keyboard user must always see where focus landed, and the muted background is what says the field is not editable.
Motion
Hover border, focus ring, and the destructive color changes are CSS transitions at --motion-fast. The error message is the one enter and exit: height and opacity on spring-settle both ways at --motion-base, because a bounce on a height change makes the fields below overshoot. The spinner runs its own 800ms turn. Autosize growth is deliberately unanimated.
Accessibility
Textarea generates the field id and wires the label htmlFor itself, so clicking the label focuses the field. An error sets aria-invalid and links the message through aria-describedby, keeping any description you passed. A loading field sets aria-busy and hides its spinner from screen readers, so the wait is announced once. The keyboard path is native: Enter inserts a newline, and Tab always moves focus out of the field rather than inserting a tab character. A submit shortcut such as Cmd+Enter belongs to your form, not to this component. required marks the label and reaches the <textarea>; the validation itself stays with your app.