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.xas 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.
size | Line | Padding | rows={3} | rows={4} |
|---|---|---|---|---|
xs | 20 | 6 | 72 | 92 |
sm | 24 | 8 | 88 | 112 |
md | 24 | 8 | 88 | 112 |
lg | 28 | 10 | 104 | 132 |
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.
| Prop | Type | Required | Description |
|---|---|---|---|
| rows | number | undefined | No | How 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. |
| maxRows | number | undefined | No | The 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. |
| children | ReactNode | No | — |
Slots
TextAreaFieldProps
The node's inherited React Native props apply too, and so do its style props — padding, margin, width and the rest.
Variants, sizes and colour
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
| Legacy | v1 |
|---|---|
<TextArea minRows> | <TextArea rows> — the same idea under React Native's word |
maxRows | maxRows, unchanged |
label="…" | <TextArea.Label>…</TextArea.Label> |
| everything else | see 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'