Textarea
Textarea: anatomy, keyboard behavior, state, and theming in Askr.
Example
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. Named types in the Type column define the accepted values.
TextareaAsChildProps
Import from @askrjs/ui/textarea.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild: true; | — | — |
children | children: JSXElement; | — | — |
disabled | disabled?: boolean | undefined; | — | — |
ref | ref?: Ref<Element>; | — | — |
tabIndex | tabIndex?: number | undefined; | — | — |
TextareaElementProps
Import from @askrjs/ui/textarea.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: false | undefined; | — | — |
children | children?: unknown; | — | — |
ref | ref?: Ref<HTMLTextAreaElement>; | — | — |
TextareaOwnProps
Import from @askrjs/ui/textarea.
| Prop | Type | Default | Description |
|---|---|---|---|
children | children?: unknown; | — | — |
disabled | disabled?: boolean | undefined; | — | — |
tabIndex | tabIndex?: number | undefined; | — | — |
TextareaProps
Import from @askrjs/ui/textarea.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | asChild?: boolean | undefined; | — | — |
children | children?: unknown; | — | — |
ref | 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 <textarea> 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 <textarea> 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 <textarea> 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 <label for>, 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 <textarea> 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.