Skip to content

Textarea

A styled multi-line text input for longer content. When you pass label, description, or error, Textarea delegates to Field for accessible labeling, descriptions, validation messaging, and field state attributes. Supports vertical resizing by default.

Source Code

Tell us a little about yourself. Max 500 characters.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    label="Bio"
    description="Tell us a little about yourself. Max 500 characters."
    placeholder="I design and build design systems..."
    rows={4}
  />
));

Import

import { Textarea } from "areia";

Usage

With Built-in Field

Use the label prop to enable the Field wrapper with label, description, and error support.

Add any additional context for the reviewer.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    id="notes"
    label="Notes"
    description="Add any additional context for the reviewer."
    placeholder="Write your notes here..."
    rows={3}
  />
));

Bare Textarea

For custom form layouts, use Textarea without label. Provide aria-label or aria-labelledby for accessibility. Use Textarea.Static(...) when composing a raw textarea inside Field manually.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    aria-label="Message"
    placeholder="Type your message..."
    rows={4}
  />
));

Examples

With Label and Description

The label prop enables the Field wrapper with automatic vertical layout.

A short description that appears on your public profile.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    id="bio"
    label="Bio"
    description="A short description that appears on your public profile."
    placeholder="I design and build design systems..."
    rows={4}
  />
));

With Error

Pass error as a string for simple error messages. Error styling is automatically applied when the error prop is truthy.

Message must be less than 500 characters.
import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    id="message-error"
    label="Message"
    placeholder="Type your message..."
    value="This message is way too long for the field limit..."
    error="Message must be less than 500 characters."
  />
));

With Validation Object

Pass error as an object with a message property when you want to keep validation metadata near the field.

Comment is required.
import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    id="comment"
    label="Comment"
    required
    placeholder="Share your thoughts..."
    error={{
      message: "Comment is required.",
      match: "valueMissing",
    }}
  />
));

Sizes

Four sizes are available: xs, sm, base, and lg. Sizes control text size, padding, and border radius — the height is determined by rows and content.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <div class="flex w-full max-w-sm flex-col gap-3">
    <Textarea
      size="xs"
      aria-label="Extra small textarea"
      placeholder="Extra small"
      rows={2}
    />
    <Textarea
      size="sm"
      aria-label="Small textarea"
      placeholder="Small"
      rows={2}
    />
    <Textarea
      size="base"
      aria-label="Base textarea"
      placeholder="Base"
      rows={2}
    />
    <Textarea
      size="lg"
      aria-label="Large textarea"
      placeholder="Large"
      rows={2}
    />
  </div>
));

Custom Row Count

Use the rows prop to control the initial visible height before the content grows.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <div class="flex w-full max-w-sm flex-col gap-3">
    <Textarea
      label="Short response"
      placeholder="A couple of lines..."
      rows={2}
    />
    <Textarea
      label="Detailed response"
      placeholder="Take your time to explain..."
      rows={5}
    />
  </div>
));

Disabled

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    label="Notes"
    placeholder="Write your notes here..."
    disabled
  />
));

Optional Field

Set required={false} to show optional text after the label.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    id="comments"
    label="Additional comments"
    required={false}
    placeholder="Anything else we should know..."
    rows={3}
  />
));

With Label Tooltip

Use labelTooltip to add additional context via a native title attribute on the label.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    id="release-notes"
    label="Release notes"
    labelTooltip="These notes are published with the release and visible to all users."
    placeholder="What changed in this version?"
    rows={4}
  />
));

Rich Label

The label prop accepts markup for richer formatting.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    id="review-notes"
    label={
      <>
        Notes for <strong>review</strong>
      </>
    }
    required
    placeholder="Add notes for the reviewer..."
    rows={3}
  />
));

Bare Textarea with Error

Error messages and descriptions can render without a visible label. Use aria-label to keep the textarea accessible.

Your feedback helps us improve the product.

Feedback is currently unavailable.
import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <Textarea
    id="feedback-error"
    aria-label="Feedback"
    placeholder="Share your feedback..."
    description="Your feedback helps us improve the product."
    error="Feedback is currently unavailable."
  />
));

Custom Resize Behavior

The textarea defaults to vertical-only resize (resize-vertical). Override it by passing a Tailwind resize utility via class.

import ilha from "ilha";
import { Textarea } from "areia";

export default ilha.render(() => (
  <div class="flex w-full max-w-sm flex-col gap-3">
    <Textarea
      label="Vertical resize"
      placeholder="Resize vertically only..."
      rows={3}
    />
    <Textarea
      label="Horizontal resize"
      placeholder="Resize horizontally only..."
      class="resize-horizontal"
      rows={3}
    />
    <Textarea
      label="No resize"
      placeholder="Cannot be resized..."
      class="resize-none"
      rows={3}
    />
  </div>
));

API Reference

Textarea accepts standard HTML textarea attributes plus the following props.

PropTypeDefaultDescription
labelunknown-Label content for the textarea. Enables the field wrapper.
labelTooltipstring-Tooltip text rendered as a native title on the label text.
descriptionunknown-Helper text displayed below the textarea.
errorunknown | { message: unknown; match?: unknown }-Error message. When truthy, error styling is automatically applied.
rowsnumber3Number of visible text rows controlling the initial height.
size"xs" | "sm" | "base" | "lg""base"Textarea size controlling text size, padding, and border radius.
variant"default" | "error""default"Visual variant. Explicit variant takes precedence over automatic error styling.
classstring-Additional CSS classes applied to the textarea.
classNamestring-Alias for class.

Value Resolution

The initial textarea content is resolved in this order of precedence:

  1. value — controlled value
  2. defaultValue — uncontrolled default
  3. children — content passed between tags

The resolved value is rendered as the textarea's text content, not as a value HTML attribute.

Validation Error Types

When using error as an object, the match property can mirror HTML5 ValidityState keys.

MatchDescription
valueMissingRequired field is empty.
tooShortValue is shorter than minLength.
tooLongValue is longer than maxLength.
trueAlways show the error, useful for server-side validation.

Accessibility

Label Requirement

Textareas should have an accessible name via one of:

  • label prop
  • placeholder plus aria-label for bare textareas
  • aria-labelledby for custom label association

Error Association

When an id is provided, descriptions and error messages are automatically associated with the textarea using aria-describedby.