|Kit

Textarea

The multi-line sibling of Input — a native textarea in the shared input shell, with bordered or filled box, focus ring, and invalid / disabled states, plus a built-in label, description, and error.

Textarea is the multi-line counterpart to Input. It renders a native <textarea> inside the same shell the whole form family uses — the bordered (or filled) box, the focus-within ring, and the invalid / disabled / readOnly states. Like the rest of the family it carries its own label, description, error, and required — no external wrapper needed — and passing error flips it to the invalid (red) state. Omit them and it still composes cleanly inside a Field.

import { Textarea } from '@42/ui-react/textarea';

<Textarea placeholder="How did the defense go?" />

Native <textarea> props (value, onChange, name, maxLength, ref, …) forward straight through; className styles the shell root and classNames overrides individual parts. The control grows automatically to fit its content (field-sizing: content); minRows and maxRows both default to 3, so an untouched Textarea reads as a comfortable multi-line box rather than a single skinny line, and resizable opts into the native manual drag handle — off by default, and flush against the shell's border when enabled.

Playground

Component

SpinnerLoading preview

Props

Code

<Component  label="Peer feedback"  size="md"  placeholder="How did the defense go?"  minRows={3}  maxRows={3}/>

Variants

Three shells. default is bordered, filled is a soft neutral surface, unstyled strips the chrome but keeps the layout.

Code

<Textarea variant="default" placeholder="Default" /><Textarea variant="filled" placeholder="Filled" /><Textarea variant="unstyled" placeholder="Unstyled" />

Sizes

Five steps — xs through xl. The control font and padding scale to match Input, so a Textarea and an Input at the same size line up.

Code

<Textarea size="xs" placeholder="xs" /><Textarea size="sm" placeholder="sm" /><Textarea size="md" placeholder="md" /><Textarea size="lg" placeholder="lg" /><Textarea size="xl" placeholder="xl" />

Min & max rows

minRows and maxRows both default to 3, so an untouched control already reads as a comfortable multi-line box instead of growing from a single skinny line. Raise minRows to start taller, and raise maxRows to allow more growth before it switches to internal scrolling — content beyond maxRows scrolls instead of growing the box further.

min-height wins whenever it conflicts with max-height, so raising minRows past the default maxRows (3) without also raising maxRows produces a fixed-height box rather than a growing one — always raise both together when you want a taller control that still grows.

Code

<Textarea placeholder="Default: 3 rows, capped at 3" /><Textarea minRows={6} maxRows={12} placeholder="Starts at six rows, grows to twelve, then scrolls" />

Resizing

resizable opts into the native manual drag handle on top of the auto-grow behavior — off by default, since the control already grows to fit what's typed. When enabled, the grip renders flush against the shell's border, and is restyled to match the kit in Chrome, Edge, and Safari (::-webkit-resizer has no equivalent in Firefox, which keeps its plain native grip — still fully functional, just unstyled).

Code

<Textarea placeholder="Not resizable (default)" /><Textarea resizable placeholder="Drag the corner to resize" />

States

invalid swaps the border and focus ring to red regardless of color (and sets aria-invalid). disabled dims the shell and blocks input; readOnly keeps full contrast and stays focusable but rejects edits.

Code

<Textarea invalid defaultValue="Too short." /><Textarea disabled defaultValue="Locked content" /><Textarea readOnly defaultValue="Read-only content" />

With label, description & error

label, description (helper text), and error are built in via Field — no external wrapper needed. Passing error flips the shell to the invalid (red) state; required adds the semantic flag and a red asterisk on the label.

A short blurb for your profile.
Bio is required

Code

<Textarea label="Bio" description="A short blurb for your profile." placeholder="Tell us about yourself…" /><Textarea label="Bio" required placeholder="Tell us about yourself…" /><Textarea label="Bio" error="Bio is required" placeholder="Tell us about yourself…" />

If you omit these props, Textarea still composes cleanly inside a Field, which propagates disabled / invalid / readOnly / required to the control automatically — exactly like Input.

import { Field } from '@42/ui-react/field';
import { Textarea } from '@42/ui-react/textarea';

<Field label="Peer feedback" description="What went well, what didn't?">
  <Textarea placeholder="How did the defense go?" />
</Field>

API

Prop

Type

Textarea forwards every other prop to the underlying <textarea> (value, defaultValue, onChange, name, maxLength, ref, …). className styles the shell root.

Accessibility

  • Pass label / description / error to wire the accessible name and aria-* relationships via the built-in Field. Omit them and it composes inside an outer Field (or your own <label htmlFor>) instead.
  • invalid sets aria-invalid on the textarea so assistive tech announces the error state.
  • The focus ring is focus-within, so it appears whenever the nested textarea is focused.
  • RTL: the shell is a plain flex layout, so padding flips automatically under dir="rtl".

On this page