Vize

textarea-autosize

Grow a textarea to fit its content.

Package @vizejs/composable/textarea-autosize
Own the source vize lib pull composable:textarea-autosize
Runtime exports useTextareaAutosize
Gzip budget 3328 B

Minimal setup

Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).

vp install @vizejs/composable

Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.

import { useTextareaAutosize } from "@vizejs/composable/textarea-autosize";

Try it

Write a release note in a growing editor

Keep a multiline draft readable as it grows, without losing the editing position when the available width changes.

Open the interactive example

What to observe: Type several lines or insert the release template. The editor grows with the text; Compact editor changes the wrapping and the height automatically. Clear draft returns to two rows. Long drafts scroll inside the editor once it reaches 12rem.

The draft stays in memory and is never saved or submitted. Bind the returned input ref with v-model and pass a template ref for the native textarea. The default height mode allows the example's CSS max-height to cap the editor; min-height is a separate minimum-sizing mode and can override a smaller CSS maximum. Padding and borders are included correctly for either box-sizing value. Programmatic changes are measured after Vue updates the textarea, and real width changes are observed automatically. Server markup keeps the declared rows; measurement and the ResizeObserver belong to the component scope. The package remains experimental.

Compare the initial and interacted states
Write a release note in a growing editor: initial state
Initial state
Write a release note in a growing editor: state after the verified interactions
After the verified interactions

Copy the example

Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.

<script setup lang="ts">
import { ref, useId, useTemplateRef } from "vue";
import { useTextareaAutosize } from "@vizejs/composable/textarea-autosize";

const draftId = useId();
const hintId = useId();
const compact = ref(false);
const draft = useTemplateRef<HTMLTextAreaElement>("draft");
const { input } = useTextareaAutosize(draft, { input: ref("") });

function insertTemplate(): void {
  input.value = [
    "Release highlights",
    "",
    "Fixed keyboard navigation in the guide catalogue.",
    "Improved compiler diagnostics for invalid templates.",
    "Verified server rendering and hydration.",
    "",
    "Upgrade notes",
    "Review the changes and run your application tests before publishing.",
  ].join("\n");
}
</script>

<template>
  <div class="composable-example">
    <p>Draft a local release note. Your text is not saved or sent to a server.</p>
    <div class="example-actions">
      <button type="button" @click="insertTemplate">Insert release template</button>
      <button type="button" @click="() => (input = '')">Clear draft</button>
      <button type="button" :aria-pressed="compact" @click="() => (compact = !compact)">
        Compact editor
      </button>
    </div>
    <div class="editor" :class="{ compact }">
      <label :for="draftId">Release notes</label>
      <textarea
        :id="draftId"
        ref="draft"
        v-model="input"
        :aria-describedby="hintId"
        rows="2"
        placeholder="What changed in this release?"
      />
      <p :id="hintId">The editor grows up to 12rem. Scroll inside it to read a longer draft.</p>
    </div>
    <output aria-live="polite">{{ input.length }} characters · local draft only</output>
  </div>
</template>

<style scoped>
.editor {
  width: 100%;
  max-width: 32rem;
  &.compact {
    max-width: 15rem;
  }
  & label {
    display: block;
    margin-bottom: 6px;
  }
  & textarea {
    box-sizing: border-box;
    display: block;
    width: 100%;
    max-height: 12rem;
    padding: 10px 12px;
    border: 2px solid #888;
    border-radius: 6px;
    font: inherit;
    line-height: 1.5;
    overflow-y: auto;
    resize: none;
  }
}
</style>

Runtime contract

Utility Category Stability SSR Hydration Cleanup Targets Host globals Uses
useTextareaAutosize dom experimental deterministic-fallback stable reactive-scope web, desktop globalThis, ResizeObserver resolveElement, useResizeObserver, tryOnScopeDispose

API

useTextareaAutosize

Grow a textarea to fit its content. On each input change (and when the element width changes), the style property is reset to auto, and scrollHeight is adjusted for the element's padding, borders and box sizing before writing the CSS height in pixels. Nothing is measured during server rendering; markup keeps its declared rows.

function useTextareaAutosize( target: MaybeElementTarget, options: UseTextareaAutosizeOptions = {}, ): TextareaAutosizeControls

Types

UseTextareaAutosizeOptions

Options for useTextareaAutosize.

Member Type Description
input? Ref<string> Text bound to the textarea. Changes trigger a resize. A fresh ref is created when omitted.
styleProp? TextareaAutosizeStyleProperty Style property written with the measured height.
onResize? (height: number) => void Called after each resize with the new height.
host? MaybeRefOrGetter<ResizeObserverHost | null | undefined> Resize-observer capability used to re-measure when the width changes.

TextareaAutosizeControls

State returned by useTextareaAutosize.

Member Type Description
input Ref<string> Text bound to the textarea (use with v-model).
triggerResize () => void Re-measure immediately.