Textarea
A multiline text field that passes through native textarea attributes.
Overview
Textarea renders a native textarea. It uses the same border, type size, focus ring, and disabled treatment as Input, with a minimum height and vertical resizing.
Preview
Customize
Installation
Add the component with the CLI. It copies the files in and installs anything they depend on.
npx vinyaas add textareaUsage
import { Textarea } from "@/components/ui/textarea";
export function MessageField() {
return (
<label htmlFor="message" className="flex flex-col gap-2 text-sm">
Message
<Textarea id="message" name="message" rows={4} placeholder="Write a message" />
</label>
);
}Examples
Basic
A multiline field. It can be resized vertically.
Character count
With maxLength and showCount, the current length and limit sit just under the field's bottom-right corner, outside the box. The counter turns destructive at the limit and is linked with aria-describedby.
23/120
Auto-grow with maxRows
With maxRows the field starts at rows lines, grows as you type, and starts scrolling once it reaches maxRows. Type a few lines to see it.
Label
Associate the label with htmlFor and id.
Description
aria-describedby connects the help text to the field.
Visible to the project team.
Rows
rows sets the initial height. The field still has a minimum height from its styles.
Required
required is the native constraint.
Disabled
A disabled textarea cannot be edited and uses the not-allowed cursor.
Invalid
aria-invalid is forwarded. Textarea does not change its border for that state.
In practice
A feedback note with a labelled textarea and Submit and Cancel actions.
API
Other attributes for the underlying element are passed through.
| Name | Type | Default | Description |
|---|---|---|---|
className | string | — | Merged onto the textarea with cn. |
showCount | boolean | false | Shows a current/maxLength counter pinned just under the field's bottom-right corner, outside the box. Without maxLength it shows only the current length. |
maxRows | number | — | Grow with the content up to this many rows, then scroll. rows sets the starting height (default 1 while growing). Turns off manual resizing. |
containerClassName | string | — | Merged onto the wrapper that holds the field and counter when showCount is set. The counter is anchored to this wrapper, so set width here rather than on className. |
rows | number | — | Native row count. The field also has a minimum height. |
cols | number | — | Native column count. |
disabled | boolean | — | Native disabled state. Uses a not-allowed cursor and reduced opacity. |
required | boolean | — | Native required state. |
placeholder | string | — | Placeholder text. It is not a label. |
value | string | — | Controlled value. Pair it with onChange. |
defaultValue | string | — | Uncontrolled initial value. |
aria-invalid | "true" | "false" | — | Passed through. Textarea does not add a separate invalid color. |
Accessibility
Textarea is a native textarea. It does not add an ARIA role.
- Name it with a label and matching id, or with aria-label.
- Placeholder text is not a replacement for the label.
- Keyboard behavior is the browser default, including line breaks.
- disabled prevents editing. Focus uses a visible focus-visible ring.
- aria-invalid and aria-describedby are passed through.