Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input OTP
- Input Group
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
- New Component
Elements
Charts
Icons
Max 500 characters
This field is required.
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
export const TextareaDemo = () => (
<div className="flex flex-col gap-6">
{/* Default */}
<Textarea placeholder="Write something…" />
{/* With label */}
<div className="flex flex-col gap-1.5">
<Label htmlFor="ta-bio">Bio</Label>
<Textarea id="ta-bio" placeholder="Tell us about yourself" className="min-h-24" />
</div>
{/* With label and helper + button */}
<div className="flex flex-col gap-1.5">
<Label htmlFor="ta-message">Message</Label>
<Textarea id="ta-message" placeholder="Your message…" className="min-h-28" />
<div className="flex items-center justify-between">
<span className="text-muted-foreground text-xs">Max 500 characters</span>
<Button size="sm">Send</Button>
</div>
</div>
{/* Disabled */}
<div className="flex flex-col gap-1.5">
<Label htmlFor="ta-disabled">Disabled</Label>
<Textarea id="ta-disabled" defaultValue="This field is read-only." disabled />
</div>
{/* Invalid */}
<div className="flex flex-col gap-1.5">
<Label htmlFor="ta-invalid">Feedback</Label>
<Textarea id="ta-invalid" placeholder="Describe the issue…" aria-invalid />
<span className="text-destructive text-xs">This field is required.</span>
</div>
</div>
);With label
<div className="flex flex-col gap-1.5">
<Label htmlFor="bio">Bio</Label>
<Textarea id="bio" placeholder="Tell us about yourself" />
</div>Custom height
Override the default min-h-16 via className.
<Textarea className="min-h-32" placeholder="Long form content…" />Auto-resize
Use the native rows attribute or a library like react-textarea-autosize for dynamic height.
<Textarea rows={3} />Disabled
<Textarea disabled defaultValue="Read-only content." />Invalid
Set aria-invalid to trigger the destructive ring.
<Textarea aria-invalid placeholder="Describe the issue…" />Props
Textarea accepts all native <textarea> props.
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder | string | — | Placeholder text |
disabled | boolean | false | Disables interaction |
aria-invalid | boolean | false | Applies destructive ring |
rows | number | — | Fixed row count |
className | string | — | Override min-h or add utilities |
Usage
import { Textarea } from "@/components/ui/textarea";
<Textarea placeholder="Write something…" />Specs
# Mint — Textarea Specs
## Component
- name: textarea
- file: registry/mint/textarea.tsx
- installs to: components/ui/textarea.tsx
- dependencies: none
- registryDependencies: none
## What is it
A multi-line text input that shares the same visual language as Input: identical
border, focus ring, invalid state, and disabled treatment. Use it whenever a
single-line Input is insufficient — forms, comments, descriptions, or any free-
form text entry. Prefer Input when content is expected to stay on one line.
## Visual identity
Transparent background with a subtle border gives it a lightweight, flat feel
consistent with Input. The 1.25rem radius and 3px focus ring match all other
form fields in Mint, ensuring visual consistency across form layouts.
## Design tokens
--input: oklch(0.88 0.04 155 / 60%) / oklch(0.32 0.05 155 / 40%) — border colour (border-input)
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155) — focus ring (focus-visible:border-ring, focus-visible:ring-ring/50)
--destructive: oklch(0.55 0.25 15) / oklch(0.704 0.191 22.216) — invalid border + ring (aria-invalid:border-destructive, aria-invalid:ring-destructive/20)
--primary: oklch(0.5 0.14 155) / oklch(0.62 0.15 155) — text selection background (selection:bg-primary)
--primary-foreground: oklch(1 0 0) / oklch(0.1 0 0) — text selection foreground (selection:text-primary-foreground)
--muted-foreground: oklch(0.52 0.06 155) / oklch(0.6 0.06 155) — placeholder text (placeholder:text-muted-foreground)
## States
- focus-visible: border switches to --ring, 3px ring at ring/50 opacity
- aria-invalid: border switches to --destructive, ring uses destructive/20 (light) or destructive/40 (dark)
- disabled: opacity-50, cursor-not-allowed, interaction blocked
- dark bg: dark:bg-input/30 — subtle tinted surface in dark mode
## Conventions
- Default minimum height is min-h-16 (64px) — override via className (e.g. className="min-h-32")
- Takes full width (w-full) by default; constrain via a wrapping element
- For dynamic height use the native rows attribute or react-textarea-autosize
- Pair with Label using htmlFor/id — Textarea has no built-in label
- Set aria-invalid to trigger the destructive ring on validation error
- Sub-components expose data-slot="textarea" for CSS targetingInstallation
$ npx shadcn add \https://gin-design.vercel.app/textarea