Textarea
The Textarea component is a styled multi-line text input. It wraps Base UI's field control rendered as a <textarea>, so it works with Field and React Hook Form the same way Input, Checkbox, and Select do — dropped inside a Field.Root it inherits label association, aria-describedby, disabled, and the invalid (error) state automatically.
Unlike Input, it has no fixed height: rows sets the visible line count (floored at min-h-16) and the user can drag the box taller, so long text stays readable instead of scrolling inside a single-line window.
Import
import { Textarea } from "@tailor-platform/app-shell";Basic Usage
<Textarea placeholder="Add a note…" />Props
Accepts Base UI's field-control props plus the standard HTML <textarea> props, minus cols and wrap — the control is always w-full, so cols could never affect its width, and every wrap value depends on cols being set. The most common:
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | - | Controlled value. |
defaultValue | string | - | Initial value when uncontrolled. |
onChange | (event) => void | - | Native change handler. |
onValueChange | (value: string, eventDetails) => void | - | Called with the next string value. |
rows | number | - | Visible line count — the height knob. |
placeholder | string | - | Placeholder text. |
disabled | boolean | false | Disables the control. |
required | boolean | false | Marks the control required for native/Field validation. |
readOnly | boolean | false | Renders the value without allowing edits. |
name | string | - | Field name for native form submission. |
className | string | - | Additional CSS classes. |
Examples
Controlled
const [note, setNote] = useState("");
<Textarea value={note} onChange={(e) => setNote(e.target.value)} placeholder="Add a note…" />;Height
rows sets the visible line count, and the user can drag the box taller for a long value.
min-h-16 (64px) is a hard floor, so rows={1} and rows={2} both render at 64px — rows only starts moving the height from 3 up. There is no supported way to go below the floor.
<Textarea rows={8} placeholder="Paste the incident log…" />The box does not auto-grow as you type.
field-sizing: contentwould do that, but the browser then ignoresrows— leaving height settable only through amin-h-*utility, which a consuming app can't rely on becauseastw:utilities exist only if they were compiled into this package's CSS.
Disabled
<Textarea value="Read-only value" disabled />Use With Field
Placed inside a Field.Root, Textarea picks up the field's label, description, and invalid/disabled state automatically — no error prop of its own.
<Field.Root name="description">
<Field.Label>Description</Field.Label>
<Textarea placeholder="What changed?" />
<Field.Description>Optional — visible to the whole team.</Field.Description>
</Field.Root>React Hook Form
Drive it with a Controller. Spreading field wires value/onChange/onBlur, and spreading fieldState onto Field.Root wires the error/invalid state.
import { useForm, Controller } from "react-hook-form";
import { Field, Textarea } from "@tailor-platform/app-shell";
const { control } = useForm({ defaultValues: { description: "" } });
<Controller
name="description"
control={control}
rules={{ required: "Description is required." }}
render={({ field, fieldState }) => (
<Field.Root name={field.name} {...fieldState}>
<Field.Label>Description</Field.Label>
<Textarea {...field} />
<Field.Error match={fieldState.invalid}>{fieldState.error?.message}</Field.Error>
</Field.Root>
)}
/>;TypeScript
import { type TextareaProps } from "@tailor-platform/app-shell";