# Textarea

> A text box that eases to the height of what you write, with a count that rolls and turns amber near the limit.

Auto-growing textareas usually snap a line taller on every wrap, or reset their height to measure and flicker. This one measures a hidden copy of the text and eases the box to it between minRows and maxRows, then scrolls. With maxLength the count rolls digit by digit and turns amber in the last tenth.

## Install

```bash
npx shadcn@latest add https://hairlineui.com/r/textarea.json
```

Needs React 19, Tailwind CSS v4 and shadcn/ui theme variables (any style, light and dark). Icons from lucide-react.

- npm: none
- Hairline UI components it needs (the CLI installs them too): `number-roll`, `ui-interaction-motion`
- Docs and live preview: https://hairlineui.com/ui/textarea
- Registry item (source): https://hairlineui.com/r/textarea.json

## Usage

```tsx
import { Textarea } from "@/components/ui/textarea";

<Textarea label="Reply" placeholder="Write a reply" minRows={2} maxRows={8} />
<Textarea label="Bio" maxLength={160} />
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `minRows / maxRows` | `number` | `3 / 10` | Height range in lines; it scrolls past maxRows. |
| `maxLength` | `number` | – | Limit; shows a rolling count beside the label. |
| `showCount` | `boolean` | `true` | Show the count when maxLength is set. |
| `label / hint / error` | `ReactNode` | – | As in Input. |
| `onValueChange` | `(value: string) => void` | – | The new text on every change. |
| `...textarea props` | `TextareaHTMLAttributes` | – | Everything a native textarea takes, including ref. |

## States

- **rest**: Three lines tall by default inside a 14px-radius hairline box.
- **growing**: Each wrap eases the box to the mirror's height (260ms); past maxRows it scrolls.
- **near limit**: With maxLength, the count turns amber at 90% and red at 100%.
- **error**: Red line, the error replaces the hint, one shake.

## What it handles

Growing a textarea by resetting its height to auto to measure it flickers and jumps. A hidden mirror measures instead, so the real box only ever eases.

- **Eased growth**: The box eases to each new height in 260ms, between minRows and maxRows.
- **A count that rolls**: Each digit rolls the way the count moved; amber in the last 10%, red at the limit.
- **Same field as the rest**: Label, hint, error and shake work exactly as in Input.
- **No layout reads per keystroke**: One ResizeObserver on the mirror, not scrollHeight on every change.

## Replaces

autosize libraries, character counter, scrollHeight hacks

## Where it fits

- **Comments and replies**: A box that starts at three lines and grows as the reply does.
- **Bio and description fields**: A limit with a count that warns before it stops you.
- **Feedback forms**: Long answers that scroll after ten lines instead of taking the page.
- **Release notes and changelogs**: Writing in a card without a fixed height.

## Notes

- Shares field.tsx with every text control: one surface, one focus ring, one way of showing hints and errors.
- Reduced motion: the height changes at once.
- Pass textareaClassName to change type; the mirror takes the same classes.

License: MIT (https://hairlineui.com/license#components)
