Aksara UI

Textarea

A native multi-line text field with a label, description, status message and counter.

import { Textarea } from '@aksornproject/aksara-ui'

Labelled field

label renders a <label> linked to the textarea, with a generated id unless you pass id. description renders under it. required sets native required and draws a star after the label. The native textarea owns value, defaultValue, onChange, validation and form submission, and ref is the HTMLTextAreaElement.

<div className="w-full max-w-spacing-384">
  <Textarea
    label="Feedback"
    description="Tell us what to improve in this lesson."
    name="feedback"
    placeholder="Enter your feedback"
    required
  />
</div>

Status and message

status takes 'error', 'warning' or 'success' and colours the border. 'default' is the same as no status. statusMessage renders under the box in the status colour, with its glyph. 'error' also sets aria-invalid, unless you pass your own.

<div className="flex w-full max-w-spacing-384 flex-col gap-spacing-16">
  <Textarea
    label="Reason for absence"
    status="error"
    statusMessage="Enter a reason before you submit."
  />
  <Textarea
    label="Lesson summary"
    defaultValue="Students practised reading short Thai poems aloud."
    status="success"
    statusMessage="Saved."
  />
</div>

Counter

counter is app-owned text at the end of the row under the box. The message grows to fill the row and the counter stays right-aligned. In a narrow field the row wraps. The component does not count anything, and maxLength passes through to the textarea unchanged.

<div className="w-full max-w-spacing-384">
  <Textarea
    label="Note to parents"
    value={value}
    onChange={(event) => setValue(event.target.value)}
    maxLength={LIMIT}
    statusMessage="Parents see this note in the app."
    counter={`${value.length} / ${LIMIT}`}
  />
</div>

Size and resizing

The textarea is 120px tall at least and 320px at most at the default scale, and longer content scrolls. It does not grow as you type. An editable textarea resizes vertically with the browser's grip. iOS may not show the grip. className and style reach the textarea, so a height or resize class you pass wins.

<div className="w-full max-w-spacing-384">
  <Textarea label="Term plan" defaultValue={PLAN} />
</div>

States

disabled keeps native disabled behaviour, so the value is left out of form data. readOnly uses a grey surface. loading sets readOnly and aria-busy and shows a spinner at the end of the first line. The value, label, description, message and counter stay visible while loading. All three states turn off resizing.

<div className="flex w-full max-w-spacing-384 flex-col gap-spacing-16">
  <Textarea
    label="Disabled"
    defaultValue="Submissions closed on 30 June."
    disabled
  />
  <Textarea
    label="Read-only"
    defaultValue="Graded by Kru Malee on 2 July."
    readOnly
  />
  <Textarea
    label="Loading"
    defaultValue="Checking your answer for spelling."
    loading
  />
</div>

Start icon

startIcon draws a decorative 20px icon before the first line of text. There is no default icon.

<div className="w-full max-w-spacing-384">
  <Textarea
    label="Answer"
    placeholder="Write your answer"
    startIcon={<IconInterfaceEssentialEditLine />}
  />
</div>

Accessibility

  • aria-describedby lists your own ids first, then the description, the message and the counter.
  • The required star is aria-hidden. The textarea carries native required.
  • The start icon and the spinner are aria-hidden.
  • No live region is added. Announce validation messages yourself if they need it.
  • The focus ring draws when the textarea has keyboard focus.

API

Textarea

className, style, events and ref go to the <textarea>. fieldClassName targets the bordered box and wrapperClassName the whole field. children is not accepted. Use value or defaultValue.

Prop

Type

Edit on GitHub

On this page