HextaUI

Textarea

Un texte multiligne qui grandit en douceur selon ce que vous écrivez, entre un nombre minimal et maximal de lignes, avec compteurs, validation et raccourci d’envoi.

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

Ajoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.

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

La boîte grandit avec votre texte, en s'adoucissant à chaque nouvelle ligne au lieu de sauter, de minRows jusqu'à maxRows, puis défile. La saisie n'est jamais retardée par l'animation.

Envoyer avec un raccourci

submitOnShortcut envoie le formulaire avec ⌘ + Enter ou Ctrl + Enter. Enter seul commence toujours une nouvelle ligne.

"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>
  )
}

Compteur de caractères

Avec maxLength dans un Field, FieldCounter compte pendant la saisie et réagit quand vous atteignez la limite.

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>
  )
}

Lignes

minRows et maxRows définissent où la croissance commence et s'arrête.

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>
  )
}

Taille fixe

autoResize={false} garde une hauteur fixe avec une poignée de redimensionnement vertical.

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"
    />
  )
}

États

Désactivé, en lecture seule et invalide. Les textareas invalides tremblent quand l'envoi d'un formulaire échoue.

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>
  )
}

De droite à gauche

Le texte, le placeholder et le curseur de saisie suivent le sens de lecture.

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

export function TextareaRtl() {
  return (
    <div dir="rtl" className="w-full max-w-sm">
      <Textarea aria-label="رسالتك" placeholder="اكتب رسالتك هنا…" />
    </div>
  )
}
ToucheAction
EnterCommence une nouvelle ligne.
⌘EnterEnvoie le formulaire, avec submitOnShortcut. Ctrl + Enter sous Windows et Linux.
  • Donnez-lui un label avec FieldLabel ou aria-label.
  • Sur écran tactile, le texte fait au moins 16px, pour qu'iOS ne zoome pas au focus.
  • Le raccourci attend pendant qu'une méthode de saisie compose, si bien que confirmer un mot japonais ou chinois n'envoie jamais le message.
  • Avec la réduction des animations, la boîte se redimensionne instantanément.
PropTypePar défaut
autoResizeS'agrandit avec le texte au lieu d'avoir une hauteur fixe.
booleantrue
minRows
number3
maxRows
number10
submitOnShortcutEnvoie le formulaire parent avec ⌘/Ctrl + Enter.
booleanfalse
shakeTremble quand l'envoi d'un formulaire le trouve invalide.
booleantrue
AttributDescription
data-slot="textarea"Le textarea.
data-autoresizePrésent quand il grandit avec son texte.
--textarea-min-rows / --textarea-max-rowsDéfini d'après minRows et maxRows.

Utilisé dans les blocks

Des blocks qui s’appuient sur Textarea.