BetaForms

TextArea

A multiline field, with the label, the hint and the error that make it usable.

Overview

A multiline field, with the label, the hint and the error that make it usable.

Not part of the 1.0 core. The fifteen are listed in the plan and this is not one of them; it ships under 1.x as a P5 component.

First example

<TextArea>
<TextArea.Label>Message</TextArea.Label>
<TextArea.Field value={message} onChangeText={setMessage} />
<TextArea.Description>Trois lignes suffisent.</TextArea.Description>
</TextArea>

Anatomy

<TextArea rows={3} maxRows={6}>
  <TextArea.Label />
  <TextArea.Field />
  <TextArea.Description />
  <TextArea.Error />
</TextArea>

It is a TextField

Not "like" one — it is one. TextArea renders the TextField's root: the same recipe, the same resolved context, the same four variants, the same size, radius, color, labelPlacement, isInvalid and isDisabled. TextArea.Label, TextArea.Description and TextArea.Error are literally the TextField's slots, re-exported rather than wrapped — a wrapper would add three components to the tree to change a displayName, and the string it would change is the one telling you the truth.

Only TextArea.Field differs, by three things: multiline, the text pinned to the top, and a height counted in lines.

That is also the reference implementation's answer — its TextArea is twenty lines rendering its TextField with the same three defaults. A component of its own is what a caller looks for; sharing every line of it is what keeps the two from drifting.

So everything in input.md applies here, and this page only covers what is added.

Usage

Basic

<TextArea>
  <TextArea.Label>Message</TextArea.Label>
  <TextArea.Field value={message} onChangeText={setMessage} />
  <TextArea.Description>Trois lignes suffisent.</TextArea.Description>
</TextArea>

rows and maxRows

<TextArea rows={3} maxRows={6}>
  …
</TextArea>

rows is the starting height, in lines. maxRows is the ceiling: past it the field stops growing and scrolls. Unset, it grows for as long as the text does — and has nothing to scroll, which is why scrollEnabled follows maxRows rather than being a prop of its own.

Both sit on the root rather than on the field, for the reason size does: the root is where the field's shape is decided, and the slot reads what it resolved.

They are raw values (R6), like color: they resolve outside the style cache from the line height the size chose, which is what lets rows={7} exist without seven entries in the cache.

sizeLinePaddingrows={3}rows={4}
xs2067292
sm24888112
md24888112
lg2810104132

A fixed height

The reference implementation's text area is a fixed 128 that scrolls rather than one that grows. That is a style prop away (R14), rather than a second API:

<TextArea>
  <TextArea.Field height={128} />
</TextArea>

Everything the TextField does

<TextArea rows={2} variant="tertiary" size="lg" labelPlacement="inside" isInvalid>
  <TextArea.Label>Message</TextArea.Label>
  <TextArea.Field />
  <TextArea.Error>Au moins vingt caractères.</TextArea.Error>
</TextArea>

labelPlacement="inside" composes: the label keeps its room at the top and the first line starts below it, because the four inside-label compounds write the same paddingTop to the text area as they do to the field.

Root props

TextAreaProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
rowsnumber | undefinedNoHow many lines tall the field starts. It is a **raw value**, not a token: it resolves outside the style cache from the line height the size chose, the same path `color` takes — which is what lets `rows={7}` exist without seven entries in the cache.
maxRowsnumber | undefinedNoThe ceiling, in lines. Past it the field stops growing and scrolls; unset, it grows with the text for as long as the text goes on.
childrenReactNodeNo

Slots

TextAreaFieldProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

This slot adds nothing to the props of its React Native node.

Variants, sizes and colour

This component adds no visual axis of its own. The values it does take are in the generated types above and in the live demo.

Accessibility

The TextField's, unchanged: the wrapper has no role, the field points at the label with aria-labelledby and at the description with aria-describedby, and aria-invalid follows isInvalid. See input.md.

Migration from legacy

Legacyv1
<TextArea minRows><TextArea rows> — the same idea under React Native's word
maxRowsmaxRows, unchanged
label="…"<TextArea.Label>…</TextArea.Label>
everything elsesee the TextField's table — it is the same component

Implementation notes

Extending it

useTextArea() carries only the two row counts — the one thing the TextField's own context cannot, because rows is not the TextField's business and a prop that does nothing in the common case reads as broken. Everything visual comes from useTextField(), because the styles were resolved there.

import { useTextField } from '@xaui/native/text-field'
import { useTextArea } from '@xaui/native/text-area'