Textarea

Atom
Multi-line input, with character count, with toolbar.

Playground

Every control sets a real prop of TextareaProps. The JSX below updates as you go.
Release notes
0
Props
label
string
placeholder
string
value
string
rows
number
4
showCount
boolean
Size
pick one
required
boolean
error
boolean
disabled
boolean
JSX · TextareaProps
<Textarea
label="Release notes"
placeholder="What changed in this version?"
rows={4}
showCount
/>

Variants

7 examples from src/atoms/textarea/textarea.md, each rendered live from its own JSX.
Description
With label
<Textarea label="Description" placeholder="A few words about this project…" />
Pre-filled
<Textarea defaultValue="Renders natively on iOS and Android and on the web." />
Bio
Required
<Textarea label="Bio" required />
0 / 280
Character counter
<Textarea showCount maxLength={280} />
Formatting toolbar
<Card flat flush style={{ overflow: "hidden" }}>
<Row tight padTight>
<Button ghost small>B</Button>
<Button ghost small>I</Button>
</Row>
<Divider />
<Textarea flush placeholder="Leave a comment…" />
</Card>
Disabled
<Textarea disabled placeholder="A few words about this project" />
Measure
<Column>
<Textarea xs start rows={2} placeholder="xs step, pinned to the start (320)" />
<Textarea lg start rows={2} placeholder="lg step, pinned to the start (512)" />
<Container lg start><Textarea rows={2} placeholder="Bare, in an lg Container: fills it (512)" /></Container>
</Column>

Sizes

Release notes
0
small
Release notes
0
default
Release notes
0
large

States

Hover, focus and press are pinned so they can be compared side by side; the rest are real props.
Release notes
0
default
Release notes
0
hover
Release notes
0
focus
Release notes
0
error
Release notes
0
disabled

Anatomy

Release notes
0
1
Label
2
Field
3
Value or placeholder
4
Counter
Dimmed parts appear only in some states or variants.

Props

37 props on TextareaProps, grouped as they are declared in source. Required props are marked *.
Prop
Type
Default
Description
Props
29 props
value
string
–
Controlled text value. Omit and use defaultValue for uncontrolled use.
onChangeText
(next: string) => void
–
Fired with the next text value on each edit.
placeholder
string
–
Placeholder shown while the field is empty.
label
string
–
The field's persistent label. Its placement is platform-adaptive: iOS and web render it ABOVE the field; Android renders the Material 3 in-container FLOATING label — a MULTILINE float pinned to the first text line at rest, floating to the top once the field is focused or filled. The label names the field for a11y.
eyebrow
boolean
false
systemFocus
boolean
false
Preserve the platform system focus outline.
dense
boolean
false
Dense paragraph-entry metrics matching the application field family.
accessibilityLabel
string
–
readOnly
boolean
false
Prevent edits while retaining focus and the resting appearance.
required
boolean
false
Marks the field as required: appends a destructive "*" to the label (decorative, never in the accessible name) and announces the field as required, through aria-required on the web and with the word in its name ("Bio, required") on iOS and Android. The star and the spoken word take effect only alongside label.
rows
number
–
Visible rows the field sizes to (4 when omitted). Sets the min height (rows times the skin's line plus the field's padding); the field still grows with content past this floor.
showCount
boolean
false
Show a live character-count line inside the field's own anatomy: an end-aligned muted caption under the control reading "N / max" (just "N" when no maxLength is set), where N is the current text length. When on, maxLength becomes a SOFT cap rather than a hard one: it is NOT forwarded to the native TextInput (a hard cap silently drops the extra keystroke, so the overage could never be seen), the user can type past it, and once N exceeds maxLength the count turns destructive AND the field enters its error state automatically (red border/underline). Leave showCount off to keep maxLength as a hard input cap.
textContentType
'none' | 'URL' | 'addressCity' | 'addressCityAndState' | 'addressState' | 'countryName' | 'creditCardNumber' | 'creditCardExpiration' | 'cre…
–
Give the keyboard and the system information about the expected semantic meaning for the content that users enter. To disable autofill, set textContentType to none. Possible values for textContentType are: - 'none' - 'URL' - 'addressCity' - 'addressCityAndState' - 'addressState' - 'countryName' - 'creditCardNumber' - 'creditCardExpiration' (iOS 17+) - 'creditCardExpirationMonth' (iOS 17+) - 'creditCardExpirationYear' (iOS 17+) - 'creditCardSecurityCode' (iOS 17+) - 'creditCardType' (iOS 17+) - 'creditCardName' (iOS 17+) - 'creditCardGivenName' (iOS 17+) - 'creditCardMiddleName' (iOS 17+) - 'creditCardFamilyName' (iOS 17+) - 'emailAddress' - 'familyName' - 'fullStreetAddress' - 'givenName' - 'jobTitle' - 'location' - 'middleName' - 'name' - 'namePrefix' - 'nameSuffix' - 'nickname' - 'organizationName' - 'postalCode' - 'streetAddressLine1' - 'streetAddressLine2' - 'sublocality' - 'telephoneNumber' - 'username' - 'password' - 'newPassword' - 'oneTimeCode' - 'birthdate' (iOS 17+) - 'birthdateDay' (iOS 17+) - 'birthdateMonth' (iOS 17+) - 'birthdateYear' (iOS 17+) - 'cellularEID' (iOS 17.4+) - 'cellularIMEI' (iOS 17.4+) - 'dateTime' (iOS 15+) - 'flightNumber' (iOS 15+) - 'shipmentTrackingNumber' (iOS 15+)
autoCapitalize
'none' | 'sentences' | 'words' | 'characters'
–
Can tell TextInput to automatically capitalize certain characters. characters: all characters, words: first letter of each word sentences: first letter of each sentence (default) none: don't auto capitalize anything https://reactnative.dev/docs/textinput#autocapitalize
autoComplete
'2fa-app-otp' | 'additional-name' | 'address-line1' | 'address-line2' | 'birthdate-day' | 'birthdate-full' | 'birthdate-month' | 'birthdate-…
–
Specifies autocomplete hints for the system, so it can provide autofill. On Android, the system will always attempt to offer autofill by using heuristics to identify the type of content. To disable autocomplete, set autoComplete to off. The following values work across platforms: - additional-name - address-line1 - address-line2 - cc-number - country - current-password - email - family-name - given-name - honorific-prefix - honorific-suffix - name - new-password - off - one-time-code - postal-code - street-address - tel - username The following values work on iOS only: - nickname - organization - organization-title - url The following values work on Android only: - birthdate-day - birthdate-full - birthdate-month - birthdate-year - cc-csc - cc-exp - cc-exp-day - cc-exp-month - cc-exp-year - 2fa-app-otp - email-otp - flight-confirmation-code - flight-number - gender - gift-card-number - gift-card-pin - loyalty-account-number - name-family - name-given - name-middle - name-middle-initial - name-prefix - name-suffix - password - password-new - postal-address - postal-address-country - postal-address-dependent-locality - postal-address-extended - postal-address-extended-postal-code - postal-address-locality - postal-address-region - postal-address-unit - promo-code - sms-otp - tel-country-code - tel-national - tel-device - upi-vpa - wifi-password - username-new
autoCorrect
boolean
false
If false, disables auto-correct. The default value is true.
autoFocus
boolean
false
If true, focuses the input on componentDidMount. The default value is false.
defaultValue
string
–
Provides an initial value that will change when the user starts typing. Useful for simple use-cases where you don't want to deal with listening to events and updating the value prop to keep the controlled state in sync.
keyboardType
KeyboardTypeOptions
–
enum("default", 'numeric', 'email-address', "ascii-capable", 'numbers-and-punctuation', 'url', 'number-pad', 'phone-pad', 'name-phone-pad', 'decimal-pad', 'twitter', 'web-search', 'visible-password') Determines which keyboard to open, e.g.numeric. The following values work across platforms: - default - numeric - email-address - phone-pad The following values work on iOS: - ascii-capable - numbers-and-punctuation - url - number-pad - name-phone-pad - decimal-pad - twitter - web-search The following values work on Android: - visible-password
inputMode
InputModeOptions
–
Works like the inputmode attribute in HTML, it determines which keyboard to open, e.g. numeric and has precedence over keyboardType.
maxLength
number
–
Limits the maximum number of characters that can be entered. Use this instead of implementing the logic in JS to avoid flicker.
onBlur
((e: BlurEvent) => void)
–
Callback that is called when the text input is blurred Note: If you are trying to find the last value of TextInput, you can use the onEndEditing event, which is fired upon completion of editing.
onFocus
((e: FocusEvent) => void)
–
Callback that is called when the text input is focused
onSubmitEditing
((e: TextInputSubmitEditingEvent) => void)
–
Callback that is called when the text input's submit button is pressed.
onKeyPress
((e: TextInputKeyPressEvent) => void)
–
Callback that is called when a key is pressed. This will be called with { nativeEvent: { key: keyValue } } where keyValue is 'Enter' or 'Backspace' for respective keys and the typed-in character otherwise including ' ' for space. Fires before onChange callbacks. Note: on Android only the inputs from soft keyboard are handled, not the hardware keyboard inputs.
returnKeyType
ReturnKeyTypeOptions
–
enum('default', 'go', 'google', 'join', 'next', 'route', 'search', 'send', 'yahoo', 'done', 'emergency-call') Determines how the return key should look.
secureTextEntry
boolean
false
If true, the text input obscures the text entered so that sensitive text like passwords stay secure. The default value is false.
testID
string
–
Used to locate this view in end-to-end tests
MeasureProps · width steps xxxs 192 · xxs 256 · xs 320 · sm 384 · md 448 · lg 512 · xl 576 · xxl 672 · xxxl 768 · wide 896 · wider 1024 · widest 1152 · page 1280, plus start to pin to the leading edge.

Accessibility and keyboard

Keyboard
Type
Edits the value
Tab
Next field
Escape
Clears when clearable
Semantics
ARIA on web
aria-invalid
aria-label
aria-labelledby
aria-live
aria-required
From the docs
Pass label (and required) to name the field: iOS and web render the label above the control, while Android floats the Material 3 in-container label at the top of the multiline box, making room for itself at the user's text size and truncating on one line when it is longer than the box while the field's accessible name stays the whole label. The required star is decorative: a screen reader hears the field as required, from aria-required on the web and from its name ("Bio, required") on iOS and Android. The box
dense preserves application paragraph fields with 13/19.5 semibold values, the dense field inset and explicit visible rows. eyebrow supplies the matching web label. accessibilityLabel overrides the visible label’s accessible name; readOnly prevents edits without dimming or removing the field from focus. systemFocus keeps the platform system focus outline.
embedded renders a bare editor inside a parent-owned field surface and focus frame, as used by Chat. It implies flush and removes the independent backing and focus outline; the parent must provide a visible focus treatment.

Platform skins

The playground's props on each platform: same tree, native shapes, type and touch sizes.
0 / 280
Web
control 10 · field 10 · card 16 · Instrument Sans
0/280
iOS
capsule controls · field 8 · card 12 · SF Pro · 44 pt
0 / 280
Android
Material 3 stadium · field 4 · card 12 · Roboto · 48 dp

Usage

<Textarea placeholder="A few words about this project" />
Pass label (and required) to name the field: iOS and web render the label above the control, while Android floats the Material 3 in-container label at the top of the multiline box, making room for itself at the user's text size and truncating on one line when it is longer than the box while the field's accessible name stays the whole label. The required star is decorative: a screen reader hears the field as required, from aria-required on the web and from its name ("Bio, required") on iOS and Android. The box fills the parent it is given; a step of its own (xs, lg, …, with start to pin it to the leading edge) or a Container step sets its measure.
The box is the single-line Input's on each platform. On the web it is the sunken well with a 1 px hairline that steps to the subtle ink on hover, takes the ring edge and a 4 px accent halo on focus and the destructive edge on error, with a 400 value on a 1.55 line; the count is an 11 px mono caption ("12 / 280"). iOS keeps its 8 pt card box with the error wash and a tabular count ("12/280"). Android uses the same opaque muted surface as the other filled fields, and its count sits at the end of the supporting-text row. Its label uses primary-text when focused and destructive-text on error. The over-limit character count also uses destructive-text on every platform. Without rows the field starts four rows tall.
Inside an overlay, Escape follows the overlay's cancellation policy. A supplied onKeyPress runs first and can call preventDefault() to handle Escape locally. Cancelling an IME candidate keeps the overlay open.

Tokens

Read from the playground render. Values follow the scheme and the ion hue.
--bg2
--fd
--fm
--ft
--ln2
--tx
--tx2
--tx3

Related components