---
title: Textarea
description: Multi-line text input with autoresize support and form field integration.
sidebar:
  order: 4
search:
  tags: [multiline, text field, autoresize, form control]
---

# Textarea

> Multi-line text input with autoresize support and form field integration.

## Import

```tsx
import { Textarea } from 'moraine'
```

## Slot Structure

Multi-line text input with optional header and footer slots.

```text
root
├── header (optional)
├── input
└── footer (optional)
```

## Examples

### Variants

Outline/subtle/ghost/none visual variants.

```tsx
function Variants() {
  const VARIANTS: TextareaVariantName[] = ['outline', 'subtle', 'ghost', 'none']

  type TextareaVariantName = Exclude<TextareaT.Variant['variant'], undefined>

  return (
    <div class="gap-3 grid lg:grid-cols-4 sm:grid-cols-2">
      <For each={VARIANTS}>
        {(variant) => <Textarea variant={variant} placeholder={`Variant: ${variant}`} rows={2} />}
      </For>
    </div>
  )
}
```

### Sizes

Textarea size scale from xs to xl.

```tsx
function Sizes() {
  const SIZES: TextareaSizeName[] = ['xs', 'sm', 'md', 'lg', 'xl']

  type TextareaSizeName = Exclude<TextareaT.Variant['size'], undefined>

  return (
    <div class="gap-3 grid lg:grid-cols-3 sm:grid-cols-2">
      <For each={SIZES}>
        {(size) => <Textarea size={size} placeholder={`Size: ${size}`} rows={2} />}
      </For>
    </div>
  )
}
```

### Autoresize

Autoresize growth with maxrows.

```tsx
function Autoresize() {
  const [value, setValue] = createSignal('Type here to see autoresize...')

  return (
    <div class="max-w-xl space-y-3">
      <Textarea
        autoResize
        maxRows={6}
        value={value()}
        onValueChange={(next) => setValue(String(next ?? ''))}
        placeholder="Start typing..."
      />
      <p class="text-xs text-muted-foreground">Characters: {value().length}</p>
    </div>
  )
}
```

### Header + Footer

Build composer-like surfaces using header/footer slots.

```tsx
function HeaderFooter() {
  const [composerValue, setComposerValue] = createSignal('Hello Moraine!')

  return (
    <div class="gap-6 grid lg:grid-cols-3">
      <Textarea
        placeholder="Ask, search or chat..."
        header={
          <>
            <span class="font-semibold">Info text</span>
            <Icon name="i-lucide-info" class="text-base ms-auto" />
          </>
        }
        classes={{
          header: 'border-b border-border',
          input: 'min-h-24',
        }}
      />

      <Textarea
        value={composerValue()}
        onValueChange={(nextValue) => setComposerValue(String(nextValue ?? ''))}
        placeholder="Write your message..."
        autoResize
        footer={
          <>
            <span>{composerValue().length}/280 characters</span>
            <Button size="xs" class="ms-auto">
              Send
            </Button>
          </>
        }
        classes={{
          footer: 'b-(t border)',
          input: 'min-h-24',
        }}
      />

      <Textarea
        placeholder="console.log('Hello, world!');"
        header={
          <>
            <Icon name="i-lucide-code" class="text-base" />
            <span>script.js</span>
          </>
        }
        footer={
          <>
            <span>Line 1, Column 1</span>
            <span class="ms-auto">JavaScript</span>
          </>
        }
        classes={{
          header: 'b-(b border)',
          footer: 'b-(t border)',
          input: 'min-h-28',
        }}
      />
    </div>
  )
}
```

## API Reference

### Attributes

#### `root`

Textarea wrapper that owns header, textarea, footer, and autoresize state.

##### Data Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| data-focused | string \| undefined | State or slot attribute exposed for styling hooks and selectors. |

#### `header`

Optional content rendered above the textarea.

#### `input`

Native textarea control used for multi-line text entry.

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-disabled | boolean \| string \| undefined | Indicates that the control is disabled. |
| aria-readonly | boolean \| string \| undefined | Indicates that the control value cannot be changed by the user. |
| aria-required | boolean \| string \| undefined | Indicates that user input is required. |

#### `footer`

Optional content rendered below the textarea.

### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| autofocus | boolean \| undefined | false | Whether to automatically focus the textarea on mount. |
| autofocusDelay | number \| undefined | 0 | Delay in milliseconds before focusing the textarea. |
| autoresize | boolean \| undefined | — | — |
| autoResize | boolean \| undefined | false | Whether the textarea should automatically resize based on content. |
| autoResizeDelay | number \| undefined | 0 | Delay in milliseconds before triggering autoresize on mount. |
| children | JSX.Element | — | Children elements, rendered inside the root below the textarea. |
| class | ClassValue | — | Class applied to the component root or trigger element. |
| classes | TextareaT.Classes \| undefined | — | — |
| defaultValue | TextareaT.Value | — | The default value of the input (uncontrolled). |
| disabled | boolean \| undefined | false | Whether the input is disabled. |
| footer | JSX.Element | — | Element to render below the textarea. |
| header | JSX.Element | — | Element to render above the textarea. |
| id | string \| undefined | — | The ID of the input element. |
| maxLength | string \| number \| undefined | — | Maximum character length for the textarea. |
| maxRows | number \| undefined | 0 | Maximum number of rows allowed during autoresize. |
| modelModifiers | M \| undefined | — | Modifiers for input processing (e.g., lazy, trim, number). |
| name | string \| undefined | — | The name of the input element, used for form submission. |
| onBlur | JSX.FocusEventHandlerUnion<HTMLTextAreaElement, FocusEvent> \| undefined | — | Native blur event handler. |
| onChange | ((value: ModifierValue<M>) => void) \| undefined | — | Callback when the textarea value change is committed. |
| onFocus | JSX.FocusEventHandlerUnion<HTMLTextAreaElement, FocusEvent> \| undefined | — | Native focus event handler. |
| onInput | JSX.InputEventHandlerUnion<HTMLTextAreaElement, InputEvent> \| undefined | — | Native input event handler. |
| onValueChange | ((value: ModifierValue<M>) => void) \| undefined | — | Callback when the textarea value changes during input. |
| placeholder | string \| undefined | — | Placeholder text for the textarea. |
| readOnly | boolean \| undefined | false | Whether the input is read-only. |
| ref | JSX.HTMLElementTags["div"] extends { ref?: infer Ref; } ? Ref : never \| undefined | — | — |
| required | boolean \| undefined | false | Whether the input is required. |
| rows | number \| undefined | 3 | Default number of rows. |
| size | "xs" \| "sm" \| "md" \| "lg" \| "xl" \| undefined | — | — |
| style | JSX.CSSProperties \| undefined | — | — |
| styles | TextareaT.Styles \| undefined | — | — |
| value | TextareaT.Value | — | The current value of the input (controlled). |
| variant | "none" \| "outline" \| "ghost" \| "subtle" \| undefined | — | — |

### ARIA

Accessibility attributes and roles emitted by the component markup.

| Attribute | Type | Description |
| --- | --- | --- |
| aria-disabled | boolean \| string \| undefined | Indicates that the control is disabled. |
| aria-readonly | boolean \| string \| undefined | Indicates that the control value cannot be changed by the user. |
| aria-required | boolean \| string \| undefined | Indicates that user input is required. |

### Data Attributes

State and slot attributes exposed for styling hooks and selectors.

| Attribute | Type | Description |
| --- | --- | --- |
| data-focused | string \| undefined | State or slot attribute exposed for styling hooks and selectors. |
| data-slot | string | Identifies the rendered slot for styling hooks and selectors. |
