# TextArea

A native multiline text field with an optional associated label.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. An agent skill is available at [/.well-known/agent-skills/site-skill.md](/.well-known/agent-skills/site-skill.md).





## Playground [#playground]

```tsx
"use client";

import { TextArea } from "@/components/ui/textarea";

export function TextareaDemo() {
  return (
    <TextArea
        label={"Message"}
        placeholder={"Write a message"}
        rows={4}
        aria-invalid={false}
    />
  );
}
```

## Installation [#installation]

Initialize shadcn in your React project, then install this component from the Vandor UI registry:

```bash
npx shadcn@latest add https://vandor-ui.vercel.app/r/textarea.json
```

The default installation target is components/ui/textarea.tsx. Review the CLI's overwrite prompt before replacing local customizations.

## Dependencies [#dependencies]

Installed automatically by the registry CLI. For manual setup:

```bash
npm install cn
```

## Usage [#usage]

```tsx
import { TextArea } from "@/components/ui/textarea";

<TextArea label="Message" placeholder="Write a message" rows={4} />;
```

`TextArea` forwards native textarea props and ref. Native resizing remains enabled.

## Examples [#examples]

The example sets a native row count and preserves the multiline input semantics.

<ComponentPreview name="textarea-demo" title="textarea-demo.tsx">
  <TextAreaDemo />
</ComponentPreview>

### Optional Storybook Stories [#optional-storybook-stories]

Install TextArea first, then run `npx shadcn@latest add https://vandor-ui.vercel.app/r/textarea-stories.json`. Stories cover rows, controlled character counts, and disabled/read-only/invalid fields.

Stories install beside TextArea under your configured UI alias and use relative imports. Use your existing matching `@storybook/react` packages (verified with 10.6.1), discover `**/*.stories.tsx`, and load your global Tailwind/theme stylesheet. No addons, extra packages, core overwrites, or Storybook config edits are required. Default-value Controls remount uncontrolled examples; CharacterCount owns local state. Storybook UI is not verified because no server exists here.

## Behavior & Accessibility [#behavior--accessibility]

The optional static label is associated with the textarea. `aria-invalid` and `aria-describedby` can expose validation state and supporting text. Disabled and read-only states follow native semantics.

## Props [#props]

| Prop               | Type                                           | Default         | Description                                               |
| ------------------ | ---------------------------------------------- | --------------- | --------------------------------------------------------- |
| aria-invalid       | boolean                                        | false           | TextArea: exposes invalid state to assistive technology.  |
| containerClassName | string                                         | undefined       | TextArea: class applied to label/control wrapper.         |
| defaultValue       | string                                         | undefined       | TextArea: initial uncontrolled value.                     |
| disabled           | boolean                                        | false           | TextArea: prevents editing and focus.                     |
| label              | string                                         | undefined       | TextArea: visible label associated with the native field. |
| labelClassName     | string                                         | undefined       | TextArea: class applied to label.                         |
| name               | string                                         | undefined       | TextArea: native form field name.                         |
| onChange           | React.ChangeEventHandler\<HTMLTextAreaElement> | undefined       | TextArea: native change callback.                         |
| placeholder        | string                                         | undefined       | TextArea: native placeholder.                             |
| readOnly           | boolean                                        | false           | TextArea: prevents editing while retaining focus.         |
| rows               | number                                         | browser default | TextArea: native visible row count.                       |
| value              | string                                         | uncontrolled    | TextArea: controlled native value.                        |

The reference lists component-specific props and selected native props, not the complete React API. Other native attributes are forwarded to the rendered element.

## Source [#source]

```tsx
"use client";

import { cn } from "cn";
import type { ComponentProps } from "react";
import * as React from "react";

import { fieldSurfaceClassName } from "./input";

export type TextAreaProps = ComponentProps<"textarea"> & {
  label?: string;
  labelClassName?: string;
  containerClassName?: string;
};

export const TextArea = ({
  className,
  containerClassName,
  id,
  label,
  labelClassName,
  ...props
}: TextAreaProps) => {
  const generatedId = React.useId();
  const textareaId = id ?? generatedId;
  const textarea = (
    <textarea
      {...props}
      id={label ? textareaId : id}
      data-slot="textarea"
      className={cn(
        "min-h-20 w-full min-w-0 rounded-md border border-input px-3 py-2 text-sm text-foreground shadow-xs outline-none transition-[color,box-shadow] placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive motion-reduce:transition-none",
        fieldSurfaceClassName,
        className
      )}
    />
  );
  if (!label && !containerClassName) {
    return textarea;
  }
  return (
    <div className={cn("grid min-w-0 gap-1.5", containerClassName)}>
      {label ? (
        <label
          htmlFor={textareaId}
          className={cn("text-sm font-medium", labelClassName)}
        >
          {label}
        </label>
      ) : null}
      {textarea}
    </div>
  );
};

```
