macbetter

Text and fields

Text Area

Enter several lines of text, in a field that grows as you type.

Grows as you type, up to six lines.
import { Field, FieldDescription, FieldLabel } from "macbetter/field"
import { TextArea } from "macbetter/text-area"

export default function Example() {
  return (
    <div style={{ width: "100%", maxWidth: 340 }}>
      <Field name="notes">
        <FieldLabel>Notes</FieldLabel>
        <TextArea placeholder="What changed in this release?" maxRows={6} />
        <FieldDescription>Grows as you type, up to six lines.</FieldDescription>
      </Field>
    </div>
  )
}

When to use it

Use a text area for notes, descriptions, messages and anything else that can run past one line. It starts at minRows and grows with the text up to maxRows, then scrolls, so a short note stays small and a long one stays readable.

For one line use a Text Field. For text mixed with people or tags use a Token Field.

Usage

import { TextArea } from "macbetter/text-area"

TextArea is a native textarea. It grows by default; pass autoResize={false} to keep it at minRows.

Rows and sending

A reply box starts at one row. submitShortcut="enter" sends its form on Return and starts a new line on Shift-Return, as in Messages; "mod+enter" sends on Command-Return instead.

import { TextArea } from "macbetter/text-area"

export default function Example() {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 12, width: "100%", maxWidth: 340 }}>
      <TextArea
        aria-label="Reply"
        minRows={1}
        maxRows={4}
        placeholder="Reply to Hollis…"
        submitShortcut="enter"
      />
      <TextArea
        aria-label="Description"
        minRows={4}
        defaultValue="Weekly revenue by region, refreshed every morning from the warehouse."
      />
    </div>
  )
}

Platform behavior

  • macOS. The bordered field with 13-point text on 16-point lines and the accent halo while editing.
  • iPadOS and iOS. The filled gray field with 17-point text on 22-point lines. A plain text area fills a list row with no chrome.
  • Every platform. There's no resize handle: the field sizes itself, natively where the browser supports field-sizing.

Accessibility

  • It stays a native textarea, with standard editing, selection and spell checking.
  • Name it with a Field label or aria-label; put limits like a character count in the Field's description so they're read with it.

API

TextArea

PropTypeDefaultDescription
minRowsnumber3The fewest lines it shows.
maxRowsnumber10The most lines it grows to before it scrolls.
autoResizebooleantrueGrows and shrinks with the text.
variant"auto" | "bordered" | "filled" | "plain""auto"Bordered on macOS and filled on touch, or a fixed look.
submitShortcut"enter" | "mod+enter"—Which keys submit its form.
onValueChange(value, details) => void—Runs on typing, a reset or a controller.

Data attributes

AttributeOnValues
data-variantTextAreaauto, bordered, filled, plain
data-filled, data-disabled, data-readonly, data-requiredTextAreaField state
data-invalidTextAreaPresent inside an invalid Field

CSS variables

VariablemacOSiOSWhat it sets
--mb-text-area-line16px22pxLine height, which the row limits multiply
--mb-text-area-pad-block5px11pxTop and bottom padding
--mb-text-area-min-rows, --mb-text-area-max-rowsFrom propsFrom propsRow limits