Textarea
Multi-line text input component with custom styles, read-only and plain-text display, and native browser resizing.
Overview
Textarea is a styled, accessible replacement for a native multi-line <textarea>. It's built on React Aria's useTextField hook, which wires aria-describedby / aria-labelledby to the ids generated for an associated label, help text, and validation feedback — the same wiring Text Input uses.
import { Textarea } from '@chassis-ui/react'
export const Example = () => {
return <Textarea placeholder="Example textarea" aria-label="Example textarea" />
}As a form field
Pass label / help / invalid / invalidFeedback / valid / validFeedback directly to Textarea to render it together with an associated FormLabel, FormHelp, and FormFeedback, already wired up — see Form Field instead when a control needs to be grouped with companion content under one label.
import { Textarea } from '@chassis-ui/react'
export const Example = () => {
return <Textarea label="Bio" help="A short description, shown on your public profile" />
}Sizing
Set the size prop to "lg" or "sm" to change the control's padding, typography, and border radius.
import { Textarea } from '@chassis-ui/react'
export const Example = () => {
return (
<>
<Textarea size="lg" placeholder="Large textarea" aria-label="Large textarea example" />
<Textarea placeholder="Default textarea" aria-label="Default textarea example" />
<Textarea size="sm" placeholder="Small textarea" aria-label="Small textarea example" />
</>
)
}Rows
Set the native rows attribute to control how many lines show before the control scrolls — independent of size, which changes padding and typography rather than height.
import { Textarea } from '@chassis-ui/react'
export const Example = () => {
return (
<>
<Textarea rows={1} placeholder="1 row" aria-label="Textarea with one row" />
<Textarea rows={4} placeholder="4 rows" aria-label="Textarea with four rows" />
</>
)
}Regardless of rows, the control never renders shorter than a single-line TextInput — the underlying .form-input class applies the same minimum height floor to both.
Disabled
Add the disabled boolean attribute to give the control a muted color. Browsers also remove disabled controls from the tab order and from form submission automatically.
import { Textarea } from '@chassis-ui/react'
export const Example = () => {
return (
<>
<Textarea
placeholder="Disabled textarea placeholder"
aria-label="Disabled textarea placeholder example"
disabled
/>
<Textarea
defaultValue="Disabled textarea value"
aria-label="Disabled textarea value example"
disabled
/>
</>
)
}Readonly
Add the readOnly boolean attribute to prevent the value from being edited. Unlike disabled, a read-only control stays focusable and is still included in form submission — only typing into it is blocked — and it keeps the same appearance as an editable control.
import { Textarea } from '@chassis-ui/react'
export const Example = () => {
return (
<>
<Textarea value="Readonly textarea" aria-label="readonly textarea example" readOnly />
<Textarea
defaultValue="Readonly disabled textarea"
aria-label="readonly disabled textarea example"
readOnly
disabled
/>
</>
)
}Plain text
Pair the plainText prop with readOnly to render the value as plain text instead of an editable-looking field — useful for read-only values that should still line up with the editable fields around them.
import { Textarea } from '@chassis-ui/react'
export const Example = () => {
return (
<Textarea
defaultValue="Shipping address confirmed by the customer."
readOnly
plainText
aria-label="Shipping address"
/>
)
}Controlled value
Pass value and onChange to control the value from external state. onChange receives the new string value directly rather than a change event, so it can be passed straight to a state setter. Omit them (optionally passing defaultValue instead) to let the component manage its own value.
import { useState } from 'react'
import { Textarea } from '@chassis-ui/react'
export const Example = () => {
const [value, setValue] = useState('')
return (
<Textarea
label="Comment"
help={`${value.length} characters`}
value={value}
onChange={setValue}
placeholder="Leave a comment"
aria-label="comment example"
/>
)
}API
Textarea
| Prop | Type | Default |
|---|---|---|
className? | string | — |
| A string of all className you want applied to the component. | ||
defaultValue? | string | — |
| The value of the textarea, uncontrolled. | ||
disabled? | boolean | — |
| Toggle the disabled state for the component. | ||
help? | ReactNode | — |
| A description for the field, rendered below the textarea. | ||
invalid? | boolean | — |
Set component validation state to invalid. Left unset, the component shows as invalid by itself
from a submit it held back (an empty required one, say) until it no longer would, or its
form is reset. Set it, to false too, and it alone decides. | ||
invalidFeedback? | ReactNode | — |
An error message for the field, rendered below the textarea when invalid is set, and after a
submit the field held back, until it no longer would. | ||
label? | ReactNode | — |
The field's caption, rendered as a FormLabel associated with this textarea. | ||
onChange? | ((value: string) => void) | — |
| Handler that is called when the value changes. | ||
plainText? | boolean | — |
Render the component styled as plain text. Removes the default form field styling and preserve the correct margin and padding. Recommend to use only along side readonly. | ||
readOnly? | boolean | — |
| Toggle the readonly state for the component. | ||
rows? | number | — |
| The number of visible text lines for the control. | ||
size? | "sm" | "lg" | — |
| Size the component sm or lg. | ||
valid? | boolean | — |
| Set component validation state to valid. | ||
validFeedback? | ReactNode | — |
A success message for the field, rendered below the textarea when valid is set. | ||
value? | string | — |
| The value of the textarea, controlled. | ||