HextaUI

Textarea

Mehrzeiliger Text, der mit dem Geschriebenen zwischen einer minimalen und maximalen Zeilenzahl sanft wächst, mit Zählern, Validierung und Absende-Tastenkürzel.

Grows with your text, then scrolls.

import { Field, FieldDescription, FieldLabel } from "@/components/ui/field"
import { Textarea } from "@/components/ui/textarea"

export function TextareaDemo() {
  return (
    <Field className="w-full max-w-sm">
      <FieldLabel>Feedback</FieldLabel>
      <Textarea placeholder="What could we do better? Keep typing — the box grows with you." />
      <FieldDescription>Grows with your text, then scrolls.</FieldDescription>
    </Field>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/textarea.json

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

import { Textarea } from "@/components/ui/textarea"
<Textarea placeholder="Write something…" />

Die Box wächst mit deinem Text und gleitet zu jeder neuen Zeile, statt zu springen, von minRows bis maxRows, und scrollt dann. Das Tippen wird nie durch die Animation verzögert.

Mit einem Tastenkürzel senden

submitOnShortcut sendet das Formular mit ⌘ + Enter oder Strg + Enter ab. Enter allein beginnt weiterhin eine neue Zeile.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { Kbd, KbdGroup } from "@/components/ui/kbd"
import { Textarea } from "@/components/ui/textarea"

export function TextareaSubmit() {
  const [comments, setComments] = React.useState<string[]>([])

  return (
    <form
      className="flex w-full max-w-sm flex-col gap-3"
      onSubmit={(event) => {
        event.preventDefault()
        const form = event.currentTarget
        const text = String(new FormData(form).get("comment") ?? "").trim()
        if (text) {
          setComments((list) => [...list, text])
          form.reset()
        }
      }}
    >
      {comments.length > 0 ? (
        <ul className="flex flex-col gap-1 text-sm">
          {comments.map((comment, index) => (
            <li
              key={index}
              className="rounded-md bg-muted px-3 py-2 whitespace-pre-wrap"
            >
              {comment}
            </li>
          ))}
        </ul>
      ) : null}
      <Textarea
        name="comment"
        aria-label="Comment"
        placeholder="Write a comment…"
        minRows={2}
        submitOnShortcut
      />
      <div className="flex items-center justify-between gap-3">
        <span className="flex items-center gap-1.5 text-xs text-muted-foreground">
          <KbdGroup>
            <Kbd keys="Mod+Enter" />
          </KbdGroup>
          to send
        </span>
        <Button type="submit" size="sm">
          Comment
        </Button>
      </div>
    </form>
  )
}

Zeichenzähler

Mit maxLength in einem Field zählt FieldCounter beim Tippen mit und gibt einen Stups, wenn du das Limit erreichst.

import { Field, FieldCounter, FieldLabel } from "@/components/ui/field"
import { Textarea } from "@/components/ui/textarea"

export function TextareaCounter() {
  return (
    <Field className="w-full max-w-sm">
      <div className="flex items-baseline justify-between gap-3">
        <FieldLabel>Bio</FieldLabel>
        <FieldCounter />
      </div>
      <Textarea name="bio" maxLength={160} minRows={2} />
    </Field>
  )
}

Zeilen

minRows und maxRows legen fest, wo das Wachsen beginnt und endet.

import { Textarea } from "@/components/ui/textarea"

export function TextareaRows() {
  return (
    <div className="grid w-full max-w-md gap-4 sm:grid-cols-2">
      <Textarea
        aria-label="One to four rows"
        minRows={1}
        maxRows={4}
        placeholder="1–4 rows"
      />
      <Textarea
        aria-label="Five to fifteen rows"
        minRows={5}
        maxRows={15}
        placeholder="5–15 rows"
      />
    </div>
  )
}

Feste Größe

autoResize={false} behält eine feste Höhe mit einem vertikalen Resize-Griff.

import { Textarea } from "@/components/ui/textarea"

export function TextareaFixed() {
  return (
    <Textarea
      aria-label="Notes"
      autoResize={false}
      minRows={4}
      placeholder="Fixed height. Drag the corner to resize."
      className="max-w-sm"
    />
  )
}

Zustände

Deaktiviert, schreibgeschützt und ungültig. Ungültige Textareas wackeln, wenn ein Formular-Submit fehlschlägt.

import { Textarea } from "@/components/ui/textarea"

export function TextareaStates() {
  return (
    <div className="grid w-full max-w-md gap-4 sm:grid-cols-3">
      <Textarea
        aria-label="Disabled"
        disabled
        placeholder="Disabled"
        minRows={2}
      />
      <Textarea
        aria-label="Read-only"
        readOnly
        defaultValue="Read-only text you can select but not change."
        minRows={2}
      />
      <Textarea
        aria-label="Invalid"
        aria-invalid
        placeholder="Invalid"
        minRows={2}
      />
    </div>
  )
}

Rechts nach links

Text, Platzhalter und Caret folgen der Leserichtung.

import { Textarea } from "@/components/ui/textarea"

export function TextareaRtl() {
  return (
    <div dir="rtl" className="w-full max-w-sm">
      <Textarea aria-label="رسالتك" placeholder="اكتب رسالتك هنا…" />
    </div>
  )
}
TasteAktion
EnterBeginnt eine neue Zeile.
⌘EnterSendet das Formular ab, mit submitOnShortcut. Strg + Enter unter Windows und Linux.
  • Beschrifte es mit FieldLabel oder aria-label.
  • Auf Touchscreens ist der Text mindestens 16px, damit iOS beim Fokussieren nicht hineinzoomt.
  • Das Tastenkürzel wartet, während eine Eingabemethode komponiert, sodass das Bestätigen eines japanischen oder chinesischen Worts nie die Nachricht sendet.
  • Bei reduzierter Bewegung ändert die Box ihre Größe sofort.
PropTypStandard
autoResizeWächst mit dem Text, statt eine feste Höhe zu haben.
booleantrue
minRows
number3
maxRows
number10
submitOnShortcutSende das übergeordnete Formular mit ⌘/Strg + Enter ab.
booleanfalse
shakeWackelt, wenn ein Formular-Submit es als ungültig findet.
booleantrue
AttributBeschreibung
data-slot="textarea"Die Textarea.
data-autoresizeVorhanden, wenn es mit seinem Text wächst.
--textarea-min-rows / --textarea-max-rowsAus minRows und maxRows gesetzt.

In Blocks verwendet

Blocks, die auf Textarea aufbauen.