---
name: textarea
title: Textarea
url: https://ui.frontic.com/components/ui/textarea.md
tier: ui
install: npx @frontic/ui add textarea
files:
  - components/ui/textarea/Textarea.vue
  - components/ui/textarea/index.ts
dependencies:
  - "@vueuse/core"
  - reka-ui
---

# Textarea

An underlined field for longer free text, such as a gift message, that grows with its content; joins a Field around it.

## Install

```sh
npx @frontic/ui add textarea
```

Used by: [Input Group](https://ui.frontic.com/components/ui/input-group.md), [Return Request](https://ui.frontic.com/components/sections/return-request.md)

npm packages: `@vueuse/core`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { Textarea } from '@/components/ui/textarea'
</script>

<template>
  <Textarea aria-label="Gift message" placeholder="Add a gift message" maxlength="200" class="max-w-sm" />
</template>
```

## API reference

### Textarea

Lets the user write longer text over several lines, such as a note or review. Renders a `<textarea>`.

Styling: `textarea`, which extends [`control`](https://ui.frontic.com/DESIGN.md#control). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `defaultValue` | `string \| number` |  | The starting value when the field is not bound with v-model. |
| `modelValue` | `string \| number` |  | The field's value, bound with v-model. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `payload: string \| number` | Fires with the new value when `modelValue` changes; `v-model` binds it. |

## Styling

| Part | Recipe |
| --- | --- |
| `default` | [`textarea`](https://ui.frontic.com/DESIGN.md#textarea) |
| `disabled` | [`disabled`](https://ui.frontic.com/DESIGN.md#disabled) |

States with a look of their own: focus, disabled and invalid.

## Source

`components/ui/textarea/Textarea.vue`

```vue
<script lang="ts">
/** Lets the user write longer text over several lines, such as a note or review. */
export default {}
</script>

<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { useVModel } from '@vueuse/core'
import { FieldControl, injectFieldRootContext, Slot } from 'reka-ui'

const props = defineProps<{
  class?: HTMLAttributes['class']
  /** The starting value when the field is not bound with v-model. */
  defaultValue?: string | number
  /** The field's value, bound with v-model. */
  modelValue?: string | number
}>()

const emits = defineEmits<{
  (e: 'update:modelValue', payload: string | number): void
}>()

const modelValue = useVModel(props, 'modelValue', emits, {
  passive: true,
  defaultValue: props.defaultValue,
})

/** Inside a Field the textarea joins it through Reka's FieldControl; elsewhere it renders alone. Both roots keep the child's own data-slot over a wrapper's, so the template takes a wrapper's from $attrs. */
const field = injectFieldRootContext(null)
</script>

<template>
  <component :is="field ? FieldControl : Slot" v-bind="field ? { asChild: true, modelValue } : {}">
    <textarea
      v-model="modelValue"
      :data-slot="$attrs['data-slot'] ?? 'textarea'"
      :class="['field-sizing-content resize-none outline-none transition-colors fui-textarea disabled:cursor-not-allowed disabled:fui-disabled', props.class]"
    />
  </component>
</template>
```
