TextArea
A labelled box for longer text that grows with what's typed up to a limit, then scrolls.
import { TextArea } from "@butteryui/react";Demo
Printed on the packing slip.
When to use it
Use it for
- A note, a description, feedback: text that can run to several lines
- Beside sm or lg fields, with size="sm" or "lg"; on a card, with variant="soft"
Not for
- A single line: use TextField
- Rich text with formatting: TextArea holds plain text
Examples
Feedback
A larger feedback box that starts at five lines and stops growing at twelve, read once the form is sent.
Gift message
An error under the box once the message runs past its limit, read out as it appears.
Printed on the card in the box, up to 60 characters.
Packing slip note
A note for the packing slip that grows as it gets longer, with the room it has left counted from its state.
Printed on the packing slip in small type.
280 characters left
Sizes and soft
A small soft note for a dense panel over a large message for a page, each line as tall as a field of its size.
No message yet
Thank you card
The message for the back of the card in each box, typed beside a preview of the card that shows it as it's written.
Variants and states
Grows, then scrolls
Three lines when empty; it grows with the text up to eight, then scrolls with faded edges.
Printed on the packing slip.
Keep it under 280 characters.
Sizes
One line of a small box is 28px tall, as an sm field is, and of a large one 48px with 16px text, as an lg field is; md keeps 22px lines.
Outline and soft
Soft sits on bg-control and shows its edge only on hover and while it has focus.
Props
TextArea
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | string | What the field asks for, shown above it. | |
hint | ReactNode | A hint under the label. | |
error | ReactNode | What's wrong, shown under the field and read with it. Leave it out while nothing is. | |
value | string | The text. Pair with onValueChange or onChange to control it. | |
defaultValue | string | The starting text when uncontrolled. | |
onValueChange | ((value: string) => void) | Called with the text on every change. | |
rows | number | 3 | The lines it shows when it's empty. Defaults to 3. |
maxRows | number | 8 | The most lines it grows to before it scrolls. Defaults to 8. |
size | "sm" | "md" | "lg" | "md" | "md" (the default), 14px text on 22px lines; "sm", 14px text on 20px lines, one line 28px tall
(--bt-h-sm), for a dense panel; or "lg", 16px text on 24px lines, one line as tall as --bt-h-cta, beside
large fields. |
variant | "outline" | "soft" | "outline" | "outline" (the default), a 1px control-border edge on bg-surface, or "soft", a bg-control fill whose
edge shows only on hover and while it has focus. |
announceError | boolean | true | Whether a new error is read out as it appears. Defaults to true; turn it off when an error summary
that takes focus reads the errors instead. |
It also takes Omit<ComponentPropsWithRef<"textarea">, "value" | "defaultValue" | "children" | "rows">.
Accessibility
Labelled and described like TextField. It grows by measuring a hidden copy of its text, never by animating or collapsing itself, fits again when its width changes, and once it reaches maxRows it scrolls inside itself with faded edges.
| Keys | What they do |
|---|---|
| Letters, numbers and symbols | Types them in; the box grows with the text up to maxRows lines, then scrolls |
| Enter | Starts a new line |
| Tab | Moves on to the next field instead of typing a tab |
Motion
It doesn't move.
With reduced motion: Nothing changes.
Layout
It fills its container's width. The box is rows lines tall while it's empty (3 by default): 22px lines at size="md", 20px lines at "sm" and 24px lines of 16px text at "lg", where one line is as tall as an sm or lg field, and grows with its text up to maxRows lines (8 by default), then scrolls inside itself with faded edges, never animating its height. Its text wraps inside the box, the label, hint and error wrap above and below it, and the whole box is the hit area.
Related
See it in the roastery demo. See it in the Deckhand demo.