Use Textarea for descriptions, comments, and messages needing more than one line. It preserves native textarea behavior. Validation, character limits, and submission handling remain application responsibilities.
Live preview
import { Textarea } from "@/components/ui/textarea";
export default function TextareaDemo() {
return (
<div className="grid w-full max-w-md gap-2">
<label htmlFor="release-notes" className="text-sm font-heading leading-none select-none">
Release notes
</label>
<Textarea
id="release-notes"
name="releaseNotes"
rows={4}
maxLength={280}
placeholder="Summarize what changed in this release."
aria-describedby="release-notes-help"
/>
<p id="release-notes-help" className="text-sm text-foreground/70">
Keep the summary concise and describe user-visible changes.
</p>
</div>
);
}
#Installation
Complete the base setup. For manual installation:
First time? Install the shared base once.
npx shadcn@latest add https://neobrutal-ui.andongmin.com/r/textarea.jsonCreate components/ui/textarea.tsx:
import * as React from "react";
import { cn } from "@/lib/utils";
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
return (
<textarea
data-slot="textarea"
className={cn(
"flex min-h-[80px] w-full rounded-base border-2 border-input bg-secondary-background px-3 py-2 text-sm font-base text-foreground selection:bg-main selection:text-main-foreground placeholder:text-foreground/50 focus-visible:border-ring focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
{...props}
/>
);
}
export { Textarea };
#Usage
import { Textarea } from "@/components/ui/textarea";
export default function ProjectDescription() {
return (
<div className="grid max-w-md gap-2">
<label htmlFor="description">Project description</label>
<Textarea
id="description"
name="description"
rows={4}
maxLength={500}
aria-describedby="description-help"
/>
<p id="description-help" className="text-sm">
Describe the project in 500 characters or fewer.
</p>
</div>
);
}#API reference
| Prop | Purpose | Default |
|---|---|---|
value / onChange | Controlled string and native event | Uncontrolled |
defaultValue | Initial text | Empty |
rows | Initial visible rows | Browser default |
maxLength | Maximum accepted characters | Unset |
disabled / readOnly / required | Native constraints | false |
id / name / aria-describedby | Label, submission key, supporting text | Unset |
#Accessibility
Keep a visible label and useful instructions outside the field. Associate character limits and errors with aria-describedby. Preserve resizing unless an equally usable alternative exists. Placeholder text is not a sufficient explanation.