Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

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.

rows3

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.

Thanks for buying from Halfmoon.

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.

Caption: Keep it under 280 characters.

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

PropTypeDefaultDescription
label

required

stringWhat the field asks for, shown above it.
hintReactNodeA hint under the label.
errorReactNodeWhat's wrong, shown under the field and read with it. Leave it out while nothing is.
valuestringThe text. Pair with onValueChange or onChange to control it.
defaultValuestringThe starting text when uncontrolled.
onValueChange((value: string) => void)Called with the text on every change.
rowsnumber3The lines it shows when it's empty. Defaults to 3.
maxRowsnumber8The 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.
announceErrorbooleantrueWhether 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.

KeysWhat they do
Letters, numbers and symbolsTypes them in; the box grows with the text up to maxRows lines, then scrolls
EnterStarts a new line
TabMoves 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.

See it in the roastery demo. See it in the Deckhand demo.