# Textarea

> Textarea: anatomy, keyboard behavior, state, and theming in Askr.

Source: [https://askrjs.com/docs/components/textarea](https://askrjs.com/docs/components/textarea)

Status: stable. Packages: @askrjs/ui/textarea, @askrjs/themes/textarea.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

```tsx
import { Button, Field, FieldHint, FieldLabel, Input } from '@askrjs/themes/components';

<Field>
  <FieldLabel htmlFor="project-name">Project name</FieldLabel>
  <Input id="project-name" name="name" required />
  <FieldHint>Shown to everyone in the workspace.</FieldHint>
  <Button type="submit">Save project</Button>
</Field>
```

## Published props

Generated from the TypeScript declarations shipped by the installed package.

### `TextareaAsChildProps`

Import from `@askrjs/ui/textarea`.

- `asChild: true;`
- `children: JSXElement;`
- `disabled?: boolean | undefined;`
- `ref?: Ref<Element>;`
- `tabIndex?: number | undefined;`

### `TextareaElementProps`

Import from `@askrjs/ui/textarea`.

- `asChild?: false | undefined;`
- `children?: unknown;`
- `ref?: Ref<HTMLTextAreaElement>;`

### `TextareaOwnProps`

Import from `@askrjs/ui/textarea`.

- `children?: unknown;`
- `disabled?: boolean | undefined;`
- `tabIndex?: number | undefined;`

### `TextareaProps`

Import from `@askrjs/ui/textarea`.

- `asChild?: boolean | undefined;`
- `children?: unknown;`
- `ref?: ((value: Element | null) => void) | { current: Element | null; } | ((value: HTMLTextAreaElement | null) => void) | { current: HTMLTextAreaElement | null; } | null | undefined;`

## Purpose

Textarea mirrors Input's approach for multi-line text: it's a thin headless wrapper around the native &lt;textarea&gt; element, handling disabled and tabIndex consistently and supporting asChild for polymorphic rendering, with no value management or auto-resize logic baked in. Use it for any multi-line free text field — comments, descriptions, messages — where the browser's native resize handle and scrolling behavior are enough.

## Install and import

Textarea needs @askrjs/ui and @askrjs/askr for its headless behavior, with @askrjs/themes as an optional add-on for the default look. Import it headless via `import { Textarea } from '@askrjs/ui/textarea'`, or grab the same component from `@askrjs/themes/textarea` when you want styling included. As with Input, the themes package re-exports the @askrjs/ui Textarea directly instead of wrapping it in its own styled component.

## Live examples

A default textarea next to a disabled one shows the baseline styling and cursor state, and an aria-invalid example demonstrates the validation border swap. Try resizing one by its handle to confirm the default CSS leaves vertical resize enabled rather than locking the box to a fixed height.

## Anatomy

Textarea renders a single native &lt;textarea&gt; and nothing around it — no counter, no auto-grow wrapper, no label. If you need a character counter or a label/hint layout, compose it with Field and Label rather than expecting Textarea to provide it. With asChild, its props merge onto the one child you supply instead of it rendering its own element.

## State model

Textarea has no state of its own beyond the disabled and tabIndex props it exposes — value, defaultValue, and onInput are ordinary native &lt;textarea&gt; attributes passed straight through, so controlled versus uncontrolled is determined by which of those you use, exactly like a plain HTML textarea.

## Keyboard and accessibility

Textarea inherits native keyboard behavior (character input, Tab to leave the field, Enter for a newline rather than submission) without any extra ARIA from the component. It supports the same labeling paths as Input — a &lt;label for&gt;, aria-label, or aria-labelledby — defaults to tabIndex 0, and moves to tabIndex -1 when disabled.

## Styling and tokens

The themed textarea targets [data-slot="textarea"], setting a --_textarea-min-height of 5rem, border and focus-ring tokens shared with Input, and resize: vertical so users can grow the box but not shrink it below its minimum. Like Input, an aria-invalid="true" attribute switches the border color to the danger token automatically — there's no separate error-state prop to set.

## API

TextareaElementProps extends native &lt;textarea&gt; attributes (minus children and ref) with disabled?: boolean, tabIndex?: number, asChild?: false, and a ref typed to HTMLTextAreaElement; TextareaAsChildProps swaps those for asChild: true and a single required child. There's no Textarea-specific prop beyond what the native element and disabled/tabIndex already cover — everything else is standard HTML textarea behavior (rows, cols, maxLength, and so on).

## Edge cases

Because the min-height is set in CSS rather than the rows attribute, a Textarea with very little content still reserves 5rem of vertical space by default — set rows or override the CSS variable if you need a shorter starting height. Unlike Input's asChild, which merges props onto whatever single child you pass with no runtime check, Textarea's asChild actively verifies the child is a native `<textarea>` and throws `Textarea \`asChild\` requires a native <textarea> host.` if it isn't — a wrong target fails loudly here instead of silently dropping props.

## Related pages

See Input for the single-line equivalent and the same asChild pattern, and Form, Label, Field, and Input Group for pairing Textarea with labels and validation messaging.

## Documentation navigation

[Previous](https://askrjs.com/docs/components/input/index.md) | [Next](https://askrjs.com/docs/components/checkbox/index.md)
