TextArea

Primitive multiline text input component that accepts standard HTML attributes

Import

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

For validation, labels, and error messages, see TextField.

Usage

"use client";

import {TextArea} from "@heroui/react";

export function Basic() {
  return (
    <TextArea
      aria-label="Quick project update"
      className="h-32 w-96"
      placeholder="Share a quick project update..."
    />
  );
}

Controlled

Characters: 0 / 280
"use client";

import {Description, TextArea} from "@heroui/react";
import React from "react";

export function Controlled() {
  const [value, setValue] = React.useState("");

  return (
    <div className="flex w-96 flex-col gap-2">
      <TextArea
        aria-describedby="textarea-controlled-description"
        aria-label="Announcement"
        placeholder="Compose an announcement..."
        value={value}
        onChange={(event) => setValue(event.target.value)}
      />
      <Description id="textarea-controlled-description">
        Characters: {value.length} / 280
      </Description>
    </div>
  );
}

Rows and Resizing

"use client";

import {Label, TextArea} from "@heroui/react";

export function Rows() {
  return (
    <div className="flex w-96 flex-col gap-4">
      <div className="flex flex-col gap-2">
        <Label htmlFor="textarea-rows-3">Short feedback</Label>
        <TextArea
          aria-label="Short feedback"
          id="textarea-rows-3"
          placeholder="This week's highlights..."
          rows={3}
        />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="textarea-rows-6">Detailed notes</Label>
        <TextArea
          aria-label="Detailed notes"
          id="textarea-rows-6"
          placeholder="Write out the full meeting notes..."
          rows={6}
          style={{resize: "vertical"}}
        />
      </div>
    </div>
  );
}

Styling

Passing Tailwind CSS classes

import {Label, TextArea} from '@heroui/react';

function CustomTextArea() {
  return (
    <div className="flex flex-col gap-2">
      <Label htmlFor="custom-textarea">Message</Label>
      <TextArea
        id="custom-textarea"
        className="rounded-xl border border-border/70 bg-surface-2 px-4 py-3 text-sm leading-6 shadow-sm"
        placeholder="Let us know how we can help..."
        rows={5}
        style={{resize: "vertical"}}
      />
    </div>
  );
}

Customizing the component classes

Override the shared .textarea class once with Tailwind's @layer components.

@layer components {
  .textarea {
    @apply rounded-xl border border-border bg-surface-2 px-4 py-3 text-sm leading-6 shadow-sm;

    &:hover,
    &[data-hovered="true"] {
      @apply bg-surface-3 border-border/80;
    }

    &:focus-visible,
    &[data-focus-visible="true"] {
      @apply border-primary ring-2 ring-primary/20;
    }

    &[data-invalid="true"] {
      @apply border-danger bg-danger-50/10 text-danger;
    }
  }
}

CSS Classes

  • .textarea – Underlying <textarea> element styling

Interactive States

  • Hover: :hover or [data-hovered="true"]
  • Focus Visible: :focus-visible or [data-focus-visible="true"]
  • Invalid: [data-invalid="true"]
  • Disabled: :disabled or [aria-disabled="true"]

API Reference

TextArea Props

TextArea accepts all standard HTML <textarea> attributes plus the following:

PropTypeDefaultDescription
classNamestring-Tailwind classes merged with the base styles.
rowsnumber3Number of visible text lines.
colsnumber-Visible width of the text control.
valuestring-Controlled value for the textarea.
defaultValuestring-Initial uncontrolled value.
onChange(event: React.ChangeEvent<HTMLTextAreaElement>) => void-Change handler.
placeholderstring-Placeholder text.
disabledbooleanfalseDisables the textarea.
readOnlybooleanfalseMakes the textarea read-only.
requiredbooleanfalseMarks the textarea as required.
namestring-Name for form submission.
autoCompletestring-Autocomplete hint for the browser.
maxLengthnumber-Maximum number of characters.
minLengthnumber-Minimum number of characters.
wrap'soft' | 'hard'-How text wraps when submitted.

For validation props like isInvalid, isRequired, and error handling, use TextField with TextArea as a child component.