Textarea

A multi-line input for entering longer text. Supports clear editing and flexible sizing for different content needs.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { Textarea } from 'frappe-ui'

const value = ref('')
</script>

<template>
  <Textarea v-model="value" placeholder="Write something..." />
</template>

Variants

vue
<script setup lang="ts">
import { Textarea } from 'frappe-ui'
</script>

<template>
  <div class="flex flex-col gap-4 w-full max-w-sm">
    <Textarea variant="subtle" label="Subtle" placeholder="Write something..." />
    <Textarea variant="outline" label="Outline" placeholder="Write something..." />
    <Textarea variant="ghost" label="Ghost" placeholder="Write something..." />
  </div>
</template>

Sizes

vue
<script setup lang="ts">
import { Textarea } from 'frappe-ui'
</script>

<template>
  <div class="flex flex-col gap-4 w-full max-w-sm">
    <Textarea size="sm" label="Small" placeholder="Write something..." />
    <Textarea size="md" label="Medium" placeholder="Write something..." />
    <Textarea size="lg" label="Large" placeholder="Write something..." />
    <Textarea size="xl" label="Extra large" placeholder="Write something..." />
  </div>
</template>

Labeling

label, description, error, and required are wired into the underlying textarea via the shared labeling contract. Setting error suppresses the description and applies aria-invalid="true".

Tell us a bit about yourself.

vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Checkbox, Textarea } from 'frappe-ui'

const bio = ref('')
const required = ref(true)
const showError = ref(false)

const error = computed(() => (showError.value ? 'Bio cannot be empty.' : ''))
</script>

<template>
  <div class="flex gap-8 items-start">
    <Textarea
      v-model="bio"
      label="Bio"
      description="Tell us a bit about yourself."
      :error="error"
      :required="required"
      placeholder="Write something..."
      class="w-72"
    />
    <div
      class="flex flex-col gap-2 items-start border-l border-outline-gray-2 pl-6"
    >
      <Checkbox v-model="required" label="required" />
      <Checkbox v-model="showError" label="show error" />
    </div>
  </div>
</template>

States

vue
<script setup lang="ts">
import { Textarea } from 'frappe-ui'
</script>

<template>
  <div class="flex flex-col gap-4 w-full max-w-sm">
    <Textarea label="Default" placeholder="Write something..." />
    <Textarea label="Required" required placeholder="Write something..." />
    <Textarea label="Disabled" disabled placeholder="Write something..." />
    <Textarea
      label="With error"
      error="Bio cannot be empty."
      placeholder="Write something..."
    />
  </div>
</template>

API Reference

Show types
typescript
import type { InputSize, InputVariant } from '../../composables/inputTypes'
import type { InputLabelingProps } from '../../composables/useInputLabeling'

export interface TextareaProps extends InputLabelingProps {
  /** Controls the visual size of the textarea. */
  size?: InputSize

  /** Visual style variant. */
  variant?: InputVariant

  /** Placeholder text shown when empty. */
  placeholder?: string

  /** Disables user interaction. */
  disabled?: boolean

  /** Bound value of the textarea. */
  modelValue?: string

  /** Debounce delay (ms) before emitting value updates. */
  debounce?: number

  /** Number of visible text rows. */
  rows?: number
}

export interface TextareaEmits {
  /** Fired when the textarea value changes. */
  'update:modelValue': [value: string]
}
size
= "sm"
InputSize

Controls the visual size of the textarea.

variant
= "subtle"
InputVariant

Visual style variant.

placeholder
string

Placeholder text shown when empty.

disabled
boolean

Disables user interaction.

modelValue
string

Bound value of the textarea.

debounce
number

Debounce delay (ms) before emitting value updates.

rows
= 3
number

Number of visible text rows.

label
string

Label rendered above (or beside, for binary controls) the input.

description
string

Helper text rendered below the input. Hidden when `error` is set.

error
string | FrappeUIError

Error message rendered below the input. When set, the control receives `aria-invalid="true"` and `data-state="invalid"`. May be either a string or an `Error` object whose `messages?: string[]` is rendered as stacked lines (with `Error.message` as the fallback).

required
boolean

Marks the field as required. Renders an asterisk next to the label and forwards `required` / `aria-required` to the underlying control.

id
string

HTML id of the underlying control. Auto-generated via `useId()` if omitted.

label
{ required: boolean; }

Overrides the rendered label content. Receives `{ required }`.

description

Overrides the rendered description content.

update:modelValue
[value: string]

Fired when the model value changes.