HextaUI

useAutosize

Agrandit un textarea selon ce que vous écrivez, entre sa hauteur minimale et maximale, en animant chaque changement sans jamais toucher au texte.

"use client"

import * as React from "react"

import { useAutosize } from "@/hooks/use-autosize"

export function UseAutosizeDemo() {
  const ref = React.useRef<HTMLTextAreaElement>(null)
  useAutosize(ref, true, undefined)

  return (
    <textarea
      ref={ref}
      rows={1}
      aria-label="Note"
      placeholder="Write a note. It grows up to six lines."
      className="max-h-[calc(6lh+1rem)] min-h-[calc(1lh+1rem)] w-full max-w-sm resize-none rounded-lg bg-muted px-3 py-2 text-sm/6 outline-none placeholder:text-muted-foreground focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden pointer-coarse:text-touch"
    />
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/use-autosize.json

Ajoute le hook et tout ce dont il dépend à votre projet.

import { useAutosize } from "@/hooks/use-autosize"
const ref = React.useRef<HTMLTextAreaElement>(null)
useAutosize(ref, true, value)

<textarea
  ref={ref}
  rows={1}
  value={value}
  onChange={(event) => setValue(event.target.value)}
  className="min-h-[calc(1lh+1rem)] max-h-[calc(8lh+1rem)] resize-none"
/>

<Textarea autoResize> utilise déjà ce hook. Appelez-le vous-même quand vous rendez votre propre <textarea>, comme un compositeur de chat ou un éditeur en ligne.

À chaque changement, le hook définit la hauteur à auto, lit le scrollHeight naturel et le borne au min-height et au max-height CSS de l'élément. Tout cela se passe avant le rendu, vous ne voyez donc jamais l'image réduite. Les bornes restent dans votre CSS : elles peuvent être responsives ou utiliser lh pour compter des lignes.

min-h-[calc(2lh+1rem)]   at least two lines plus the vertical padding
max-h-[calc(8lh+1rem)]   then scroll after eight
max-h-64                 or any fixed length
  • Les changements de hauteur s'animent sur 180ms, et un changement qui survient en pleine animation repart de la hauteur actuelle, si bien qu'une frappe rapide ne saute jamais. Avec la réduction des animations, la hauteur change d'un coup.
  • Au-delà de max-height, le textarea défile. Sinon sa barre de défilement reste masquée, pour ne pas clignoter à chaque nouvelle ligne.
  • Il se réajuste quand la largeur change, car un texte qui passe à la ligne autrement change la hauteur.
  • Il écoute input sur window et ne fait que mesurer. Il ne lit ni n'écrit jamais la valeur : les frappes ne sont donc jamais retardées ni perdues.

Valeurs contrôlées

Taper déclenche input, mais définir value depuis le code, non. Passez la valeur en troisième argument et le textarea se réajuste quand vous insérez un modèle ou le videz.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { useAutosize } from "@/hooks/use-autosize"

const template = `Hi team,

The release is out. Highlights:
- Faster search
- Offline drafts
- New keyboard shortcuts

Thanks!`

export function UseAutosizeControlled() {
  const ref = React.useRef<HTMLTextAreaElement>(null)
  const [value, setValue] = React.useState("")
  useAutosize(ref, true, value)

  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <textarea
        ref={ref}
        rows={1}
        aria-label="Message"
        value={value}
        onChange={(event) => setValue(event.target.value)}
        placeholder="Message"
        className="max-h-[calc(8lh+1rem)] min-h-[calc(2lh+1rem)] w-full resize-none rounded-lg bg-muted px-3 py-2 text-sm/6 outline-none placeholder:text-muted-foreground focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden pointer-coarse:text-touch"
      />
      <div className="flex gap-2">
        <Button variant="outline" size="sm" onClick={() => setValue(template)}>
          Insert template
        </Button>
        <Button variant="ghost" size="sm" onClick={() => setValue("")}>
          Clear
        </Button>
      </div>
    </div>
  )
}
  • Définissez rows={1} et resize-none. Une poignée de redimensionnement manuel lutte contre la hauteur automatique.
  • Désactiver enabled ou démonter retire la hauteur et l'overflow en ligne, et rend la taille à votre CSS.
  • Sur écran tactile, utilisez un texte d'au moins 16px dans le textarea, sinon iOS zoome au focus.
PropTypePar défaut
refLe textarea à dimensionner.
RefObject<HTMLTextAreaElement | null>–
enabledIndique s'il faut dimensionner automatiquement.
boolean–
valueLa valeur contrôlée. Passez undefined pour les textareas non contrôlés.
unknown–

Textarea et InputGroupTextarea.