HextaUI

useAutosize

Faz um textarea crescer conforme o que você escreve entre a altura mínima e a máxima, animando cada mudança sem nunca tocar no 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

Adiciona o hook e tudo de que ele depende ao seu projeto.

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> já usa este hook. Chame-o você mesmo ao renderizar seu próprio <textarea>, como um compositor de chat ou um editor inline.

A cada mudança o hook define a altura como auto, lê o scrollHeight natural e o limita ao min-height e max-height do próprio CSS do elemento. Tudo isso acontece antes da pintura, então você nunca vê o quadro recolhido. Os limites ficam no seu CSS, podendo ser responsivos ou usar lh para contar linhas.

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
  • As mudanças de altura animam em 180ms, e uma mudança que chega no meio da animação parte da altura atual, então digitar rápido nunca salta. Com movimento reduzido, a altura muda de uma vez.
  • Além de max-height o textarea rola. Caso contrário, a barra de rolagem fica oculta, para não piscar a cada nova linha.
  • Ele reajusta quando a largura muda, porque texto que quebra de outro jeito muda a altura.
  • Ele escuta input em window e apenas mede. Nunca lê nem escreve o valor, então as teclas nunca são atrasadas nem perdidas.

Valores controlados

Digitar dispara input, mas definir value pelo código não. Passe o valor como terceiro argumento e o textarea se reajusta quando você insere um modelo ou o limpa.

"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>
  )
}
  • Defina rows={1} e resize-none. Uma alça de redimensionamento manual briga com a altura automática.
  • Desligar enabled ou desmontar remove a altura e o overflow inline, devolvendo o tamanho ao seu CSS.
  • Em telas touch, use texto de pelo menos 16px no textarea, ou o iOS dá zoom ao focá-lo.
PropTipoPadrão
refO textarea a dimensionar.
RefObject<HTMLTextAreaElement | null>–
enabledSe deve dimensionar automaticamente.
boolean–
valueO valor controlado. Passe undefined para textareas não controlados.
unknown–

Textarea e InputGroupTextarea.