Skip to content

Text Area

A multi-line text input with label, help text, validation, optional auto-resize and a character counter.

Preview

Installation

pnpm add @syntara/react @syntara/tokens
import { TextArea } from '@syntara/react';

Usage

import { TextArea } from '@syntara/react';

export function Notes() {
  return <TextArea label="What happened?" description="Include dates and amounts." maxLength={500} />;
}

Examples

Character counter

Shown when maxLength is set; announced only near and at the limit.

Auto-resize

Grows with the text up to maxRows, then scrolls.

Invalid and disabled

Tell us why you’re disputing this charge.

Accessibility

KeysAction
TabMoves focus to the text area.
EnterInserts a new line.
  • The visible counter is aria-hidden; a polite live region announces only when 10% of the limit remains and when the limit is reached.
  • The counter turns warning then danger colour, and the number itself changes, so it is not colour-only.
  • Live-region messages are English; wrap or fork to localise.

Guidelines

Do

  • Size rows to the expected answer length.
  • Use maxLength with a counter when there is a real limit (e.g. a statement note).

Don’t

  • Don't use a text area for single-line data like names or amounts.
  • Don't set maxRows so low that people can't review what they wrote.

API reference

TextArea

label
ReactNode

Visible label, linked to the control. Without one, pass aria-label.

description
ReactNode

Help text, linked with aria-describedby.

errorMessage
string | ((validation: ValidationResult) => string)

Shown with an icon when invalid. Defaults to the browser or validate() message.

placeholder
string

Example text. Not a substitute for a label.

rows
number

Visible rows; also the minimum height when autoResize is on.

Default 3

autoResize
boolean

Grow with the content instead of showing a resize handle.

Default false

maxRows
number

With autoResize, stop growing after this many rows.

maxLength
number

Hard limit (native maxlength) and turns on the counter.

value / defaultValue
string

Controlled / uncontrolled value.

onChange
(value: string) => void

Called on every edit.

isRequired
boolean

Marks the field required (native required + a decorative asterisk on the label).

Default false

isInvalid
boolean

Forces the invalid state (aria-invalid, danger border, error message).

Default false

isDisabled
boolean

Disables the field.

Default false

validate
(value) => string | string[] | true | null | undefined

Custom validation; return an error message to show.

inputRef
Ref<HTMLTextAreaElement>

Ref to the underlying textarea.

Tokens

The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.

Colour12
border.defaultborder.strongfeedback.danger.fgfeedback.danger.solidfeedback.warning.fgfocus.ringsurface.defaultsurface.sunkentext.defaulttext.disabledtext.subtlesurface.canvas
Type4
font.size.mdfont.size.xsfont.weight.mediumline-height.normal
Space and size5
control-heightcontrol-padding-inlinespace.2space.3space.1
Shape1
radius.field
Motion6
motion.duration.fastmotion.duration.normalmotion.easingmotion.easingOutmotion.duration.springmotion.spring