> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Textarea - Web

> Textareas are form elements that allow users type multiple lines of text.

> [!NOTE]
>
> For development, import `Textarea` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=224-1393).

![](https://mintcdn.com/servicetitan/H5FwKyiqhPVZ1UJQ/images/docs/web/components/shared/overview-of-textarea.png?fit=max\&auto=format\&n=H5FwKyiqhPVZ1UJQ\&q=85\&s=302920ca27acf24c08b3cf11318311bf)

## Anatomy

The Textarea consists of four primary elements that work together to allow users to type multiple lines of text.

![Anatomy of a
Textarea](https://mintcdn.com/servicetitan/ar8TJanaRSxOpbCO/images/docs/web/components/textarea/design/anatomy-of-a-textarea.png?fit=max\&auto=format\&n=ar8TJanaRSxOpbCO\&q=85\&s=8de0c27c0f8523a54f2f5e890a9cb5b4)

1. Label
2. Help text
3. Field
4. Resize element

## Options

Textarea supports label, description text, counter, required, placeholder, and user resizing configurations to accommodate various multi-line text input scenarios.

### Label

textarea-label

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-label?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Label" />;
}

export default App;
```

A Textarea should almost always have a label, as it provides essential information about what to do with the Textarea.

### Description text

textarea-description

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-description?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Label" description="Description text" />;
}

export default App;
```

Description text provides information to help the user avoid errors.

### Counter

textarea-showcounter

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-showcounter?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Label" showCounter maxLength={100} />;
}

export default App;
```

A counter can be displayed if there is a max length.

### Required

textarea-required

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-required?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Label" labelProps={{ required: true }} />;
}

export default App;
```

A visual indicator applied to the label.

### Placeholder text

textarea-placeholder

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-placeholder?file=src/App.tsx)

While generally not recommended to use, the Textarea can utilize placeholder text if needed. Don't use placeholder as a replacement for the label.

### User resizing

textarea-disableresize-false

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-disableresize-false?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Resize enabled" />;
}

export default App;
```

textarea-disableresize-true

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-disableresize-true?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Resize disabled" disableResize />;
}

export default App;
```

By default, a user can resize the Textarea in any direction, as is the standard for normal Textareas. This can also be turned off if there are problems with allowing a user to resize, such as in a tight layout.

## Behavior

Textarea responds to user interaction with distinct visual states, height control, and overflow handling.

### Visual States

#### Default

textarea-defaultvalue

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-defaultvalue?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Label" defaultValue="Text in Textarea" />;
}

export default App;
```

#### Disabled

textarea-disabled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-disabled?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Label" defaultValue="Text in Textarea" disabled />;
}

export default App;
```

#### Readonly

textarea-readonly

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-readonly?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Label" defaultValue="Text in Textarea" readOnly />;
}

export default App;
```

Readonly prevents edits to the Textarea, but still allows a user to copy text and focus the element, unlike with disabled styling.

#### Error

textarea-error

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-error?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Textarea
      label="Label"
      defaultValue="Text in Textarea"
      error="Error message"
    />
  );
}

export default App;
```

The error message provided would replace any help text that might exist below the label.

### Height (Rows)

textarea-height

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-height?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea, Flex } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="6">
      <Textarea label="Label" defaultValue="Height set to 1 row" rows={1} />
      <Textarea
        label="Label"
        defaultValue={"Height set to" + "\n" + "3" + "\n" + "rows of text"}
        rows={3}
      />
    </Flex>
  );
}

export default App;
```

The height of the Textarea is controlled by the rows property native to the Textarea, and is by default 3 rows tall. See Overflow handling for additional row options.

### Overflow

The Textarea overflows like a typical html textarea. When the rows of text exceed the Textarea's height or row count, it will scroll.

textarea-overflow

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-overflow?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Textarea
      label="Label"
      defaultValue={
        "Row text 1" +
        "\n" +
        "Row text 2" +
        "\n" +
        "Row text 3" +
        "\n" +
        "Row text 4" +
        "\n" +
        "Row text 5"
      }
      rows={3}
    />
  );
}

export default App;
```

A Textarea with 3 rows and no additional properties. A scrollbar appears with 4 or more row items.

## Usage Guidelines

| Standard | Requirement |
| - | - |
| DoLonger freeform text | Use the Textarea when a user needs to add a longer, freeform amount of text, typically spanning over multiple lines. They are usually used as part of a [Form](https://anvil.servicetitan.com/docs/patterns/forms/web.md). |

### When to Use

Use the Textarea when:

* **Longer, freeform text** – Users enter text that typically spans multiple lines.
* **Form input** – A [Form](https://anvil.servicetitan.com/docs/patterns/forms/web.md) collects a multi-line text entry.

### When not to use

Avoid using the Textarea for:

* **Single-line text** – Use a [Text Field](https://anvil.servicetitan.com/docs/components/text-field/web.md) when the entry fits on one line.

## Content

Content within Textarea should clearly communicate the expected input through labels and descriptions.

Most of the content guidelines from [Forms](https://anvil.servicetitan.com/docs/patterns/forms/web.md) applies to Textareas.

#### Use a label for all Textareas

A label is a short, meaningful description that clearly indicates what the user is expected to enter. Labels should be 1 to 3 words and written in title case. Labels should primarily be nouns. Avoid using labels as CTAs. A label is not inline help and shouldn't be used to provide instruction.

#### Avoid using placeholder text

Placeholder text has several accessibility issues:

* Text has low contrast which makes the text hard to read
* Text disappears as soon as the user starts typing
* Can't accommodate additional context due to limited space
* Unreliable for screen readers
* Use inline help to provide hints, formatting, and requirements

#### Show descriptions, formatting, and requirements

Description text should explain a feature or the outcome of the actions the user is about to take.

The description should adapt to the situation and context. The guidance could be focused on what is needed, or it could describe how to enter it.

textarea-description-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-description-do?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Textarea
      label="Job description"
      description="Description will be visible to the customer"
    />
  );
}

export default App;
```

> [!TIP]
>
> **Do**

textarea-description-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-description-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Job description" description="Enter a description" />;
}

export default App;
```

> [!CAUTION]
>
> **Don't**

### Keep more info short, simple, and scannable

More info should be short and easily scannable, no longer than a single phrase or short sentence.

Avoid repeating the UI text. Use sentence case, and only include period if more than one sentence is used.

For more complex or important information, consider another method of presentation, e.g., description or a link to the knowledge base.

textarea-moreinfo-do

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-moreinfo-do?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Textarea
      label="Job description"
      moreInfo="Regional settings are numbers, dates, and currencies related to your region"
    />
  );
}

export default App;
```

> [!TIP]
>
> **Do**

textarea-moreinfo-dont

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-moreinfo-dont?file=src/App.tsx)

```tsx lines theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Textarea
      label="Job description"
      moreInfo="The numbers, dates, and currencies related to your region are important because they can affect the way that your organization will make decisions for that region. Choose a regional setting that makes the most sense for your area."
    />
  );
}

export default App;
```

> [!CAUTION]
>
> **Don't**

## Keyboard Interaction

Users can navigate Textarea using standard keyboard controls.

| Key | Interaction |
| - | - |
| Tab | Focuses the Textarea |

### Accessibility

* Placeholder text is not a substitute for a label.
* When the label is not directly used for whatever reason, an annotated label should still be provided.
  * If the label exists, but is an element separate from the Textarea, these should be linked through aria-labelledby. [Refer to W3C's on how to implement this](https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA7).
  * If no label exists at all on the page, provide an annotated text label, which would utilize an aria-label. [Refer to W3C's on how to implement this](https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA8).

For more guidance on form field labels and context, see [input field context association best practices](https://anvil.servicetitan.com/docs/accessibility/labels-and-ctas.md#input-field-context-association).

## Code

**Implementation**

textarea-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Textarea
      description="Description"
      label="Label"
      placeholder="Placeholder"
      moreInfo="More info!"
    />
  );
}

export default App;
```

## Common Examples

```tsx theme={null}
import { Textarea } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <Textarea description="Description" label="Label" moreInfo="More info!" />
  );
}
```

### Getting the textarea value

#### In a form

The `Textarea` component can be used in the same way that you would use an HTML `textarea` and `label` element.

textarea-uncontrolled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-uncontrolled?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Textarea, Flex, Button } from "@servicetitan/anvil2";

function App() {
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        alert(((e.target as HTMLFormElement)[0] as HTMLInputElement).value);
      }}
    >
      <Flex direction="column" gap="2">
        <Textarea
          label="Comment"
          description="Tell us your thoughts!"
          name="comment"
        />
        <Flex alignSelf="flex-end" gap="2">
          <Button type="reset" size="small">
            Clear
          </Button>
          <Button type="submit" size="small" appearance="primary">
            Leave comment
          </Button>
        </Flex>
      </Flex>
    </form>
  );
}

export default App;
```

#### Using state

Use the `value` prop to control the value of a `Textarea` using a state management strategy.

textarea-controlled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-controlled?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Textarea, Flex, Button } from "@servicetitan/anvil2";
import { useState } from "react";

function App() {
  const [comment, setComment] = useState<string>();

  return (
    <Flex direction="column" gap="2">
      <Textarea
        label="Comment"
        description="Tell us your thoughts!"
        value={comment}
        onChange={(e) => {
          setComment(e?.target.value);
        }}
      />
      <Flex alignSelf="flex-end" gap="2">
        <Button onClick={() => setComment("")} size="small">
          Clear
        </Button>
        <Button
          onClick={() => alert(comment)}
          appearance="primary"
          size="small"
        >
          Leave comment
        </Button>
      </Flex>
    </Flex>
  );
}

export default App;
```

#### Using a ref

Use the `value` and `ref` props to control the value of a `Textarea` with a React `ref` and the `ref.current.value`.

textarea-useref

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-useref?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Textarea, Flex, Button } from "@servicetitan/anvil2";
import { useRef } from "react";

function App() {
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  return (
    <Flex direction="column" gap="2">
      <Textarea
        label="Comment"
        description="Tell us your thoughts!"
        ref={textareaRef}
      />
      <Flex alignSelf="flex-end" gap="2">
        <Button
          onClick={() => {
            if (textareaRef.current) {
              textareaRef.current.value = "";
            }
          }}
          size="small"
        >
          Clear
        </Button>
        <Button
          onClick={() => {
            if (textareaRef.current) {
              alert(textareaRef.current.value);
            }
          }}
          appearance="primary"
          size="small"
        >
          Leave comment
        </Button>
      </Flex>
    </Flex>
  );
}

export default App;
```

### Dynamic height

Use the `autoHeight` prop of the `Textarea` component to enable the element to grow to fit the text. Optionally, add `maxRows` and/or `minRows` to set height boundaries.

textarea-autoheight

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-autoheight?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Textarea
      autoHeight
      minRows={2}
      maxRows={4}
      disableResize
      label="Send a message"
    />
  );
}

export default App;
```

### Show character counter

Use the `maxLength` and `showCounter` props to display a character counter below the textarea.

textarea-showcounter

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-showcounter?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Label" showCounter maxLength={100} />;
}

export default App;
```

### Markdown in labels

The `label` prop supports inline markdown: bold (`**text**`), italic (`*text*`), bold and italic (`***text***`), highlight (`==text==`), and code (`` `text` ``).

textarea-markdownlabel

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-markdownlabel?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Flex, Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex direction="column" gap="4" style={{ maxWidth: 400 }}>
      <Textarea label="**Bold** label" placeholder="Bold" />
      <Textarea label="*Italic* label" placeholder="Italic" />
      <Textarea
        label="***Bold and italic*** label"
        placeholder="Bold and italic"
      />
      <Textarea label="==Highlight== label" placeholder="Highlight" />
      <Textarea label="`Code` label" placeholder="Code" />
    </Flex>
  );
}

export default App;
```

### Hide the label

Use `hideLabel` to visually hide the label. The `label` string is converted to an `aria-label` on the input so it remains accessible to screen readers — any inline markdown is stripped to plain text.

textarea-hidelabel

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/textarea-hidelabel?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Textarea } from "@servicetitan/anvil2";

function App() {
  return <Textarea label="Notes" hideLabel placeholder="Add a note..." />;
}

export default App;
```

**Textarea Props**

```tsx theme={null}
<Textarea
  label="Label"
  description="Description"
  hint="Hint text"
  error="Error message"
  autoHeight={false}
  disableResize={false}
  maxLength={100}
  maxRows={10}
  minRows={3}
  showCounter={true}
  required={false}
  moreInfo="More info"
  errorAriaLive="assertive"
  onChange={(e, state) => console.log(state.value)}
/>
```

## `Textarea` Props

In addition to the props listed below, the `Textarea` component can accept any valid HTML `textarea` props. Note that the `onChange` prop has an additional `state` parameter the HTML `textarea` props does not.

**onChange** `(e: ChangeEvent, state?: TextareaState) =\> void;` *required*

The `state` parameter includes `value: string`

**autoHeight** `boolean`

**description** `string | ReactElement`

**disableResize** `boolean` (default: false)

**error** `string | ReactElement | boolean`

**errorAriaLive** `"assertive" | "polite" | "off"` (default: assertive)

Use `"polite"` for live and instant validation.

**hideLabel** `boolean` (default: false)

Visually hides the label while keeping it accessible to screen readers via `aria-label`.

**hint** `string | ReactElement`

**label** `string | ReactElement`

Label for the field. Supports inline markdown formatting.

> [!WARNING]
>
> Omitting `label` is deprecated and will be required in v4.0.0. Use `hideLabel` to visually hide it. Passing `ReactElement` is also deprecated — use a plain string with inline markdown instead.

**labelProps** `Omit\<LabelProps, "children" | "moreInfo" | "required" | "id" | "htmlFor"\>`

Passed to the underlying `Label` component.

**maxLength** `number`

Required if `showCounter` is `true`.

**maxRows** `number`

Only valid if `autoHeight` is `true`.

**minRows** `number`

Only valid if `autoHeight` is `true`.

**moreInfo** `string`

Text added to info icon tooltip in the label.

**required** `boolean` (default: false)

**showCounter** `boolean`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.