HextaUI

useAutosize

Hace crecer un textarea según lo que escribes entre su altura mínima y máxima, animando cada cambio sin tocar nunca el texto.

"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

Añade el hook y todo lo que necesita a tu proyecto.

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> ya usa este hook. Llámalo tú mismo cuando renderices tu propio <textarea>, como el compositor de un chat o un editor en línea.

En cada cambio el hook pone la altura en auto, lee el scrollHeight natural y lo limita al min-height y max-height de CSS del propio elemento. Todo eso ocurre antes del pintado, así que nunca ves el fotograma colapsado. Los límites permanecen en tu CSS, por lo que pueden ser responsivos o usar lh para contar líneas.

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
  • Los cambios de altura se animan durante 180ms, y un cambio que llega a mitad de la animación parte de la altura actual, así que escribir rápido nunca salta. Con movimiento reducido la altura cambia de golpe.
  • Pasado max-height el textarea hace scroll. Si no, su barra de scroll permanece oculta, para que no parpadee en cada nueva línea.
  • Se reajusta cuando cambia el ancho, porque el texto que se ajusta de otra forma cambia la altura.
  • Escucha input en window y solo mide. Nunca lee ni escribe el valor, así que las pulsaciones nunca se retrasan ni se pierden.

Valores controlados

Escribir dispara input, pero definir value desde código no. Pasa el valor como tercer argumento y el textarea se reajusta cuando insertas una plantilla o lo vacías.

"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>
  )
}
  • Define rows={1} y resize-none. Un tirador de redimensionamiento manual choca con la altura automática.
  • Desactivar enabled o desmontar elimina la altura y el overflow en línea, devolviendo el tamaño a tu CSS.
  • En pantallas táctiles, usa texto de al menos 16px en el textarea, o iOS hace zoom al enfocarlo.
PropTipoPredeterminado
refEl textarea que se va a dimensionar.
RefObject<HTMLTextAreaElement | null>–
enabledSi se debe dimensionar automáticamente.
boolean–
valueEl valor controlado. Pasa undefined para textareas no controlados.
unknown–

Textarea e InputGroupTextarea.