Vize

resize-observer

Observe size changes of one or more reactive elements.

Package @vizejs/composable/resize-observer
Own the source vize lib pull composable:resize-observer
Runtime exports useResizeObserver, useElementSize
Gzip budget 2816 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 { useResizeObserver, useElementSize } from "@vizejs/composable/resize-observer";

Try it

Observe the size of a selected guide panel

Report the native content-box measurement of one panel and follow a changing target without observing the other panel.

Open the interactive example

What to observe: Toggle Wide first panel to change its real CSS size and delivered measurement. Resizing the second panel leaves the first panel's readout alone. Choose Second guide panel to retarget the observer. Stop size observation freezes the last delivered measurement while both native checkboxes keep resizing their panels.

A computed template target selects one actual section. The native callback reads contentRect and labels its target; its notification count counts deliveries, not every layout change. Border and padding are excluded from the reported content box. Retargeting disconnects the previous observer, and stop permanently stops its watcher and observer. A callback entry from an earlier target is ignored by this example. During SSR there is no host, support is false, and no measurement is taken. The browser host resolves after mounting, retaining the initial hydration markup. Scope disposal also disconnects observation. Panel classes and responsive maximum width remain ordinary CSS.

Compare the initial and interacted states
Observe the size of a selected guide panel: initial state
Initial state
Observe the size of a selected guide panel: 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 { computed, ref, useId, useTemplateRef } from "vue";
import { useResizeObserver } from "@vizejs/composable/resize-observer";

const panelsId = useId();
const first = useTemplateRef<HTMLElement>("first");
const second = useTemplateRef<HTMLElement>("second");
const firstWide = ref(true);
const secondWide = ref(false);
const selected = ref("first");
const target = computed(() => (selected.value === "first" ? first.value : second.value));
const observedWidth = ref(0);
const observedHeight = ref(0);
const observedName = ref("No panel observed yet");
const notifications = ref(0);
const stopped = ref(false);
const { isSupported, stop } = useResizeObserver(target, (entries) => {
  const entry = entries.find((candidate) => candidate.target === target.value);
  if (!entry) return;
  observedWidth.value = entry.contentRect.width;
  observedHeight.value = entry.contentRect.height;
  observedName.value = entry.target === first.value ? "First guide panel" : "Second guide panel";
  notifications.value += 1;
});

function stopObservation(): void {
  stop();
  stopped.value = true;
}
</script>

<template>
  <div class="composable-example">
    <label :for="`${panelsId}-target`">Observed guide panel</label>
    <select :id="`${panelsId}-target`" v-model="selected">
      <option value="first">First guide panel</option>
      <option value="second">Second guide panel</option>
    </select>
    <div class="example-actions">
      <label class="panel-size-choice"
        ><input v-model="firstWide" type="checkbox" /> Wide first panel</label
      >
      <label class="panel-size-choice"
        ><input v-model="secondWide" type="checkbox" /> Wide second panel</label
      >
    </div>
    <div class="guide-panels">
      <section
        ref="first"
        class="guide-panel"
        :class="{ wide: firstWide }"
        aria-label="First guide panel"
      >
        <strong>First guide panel</strong>
        <p>Compile a representative component.</p>
      </section>
      <section
        ref="second"
        class="guide-panel"
        :class="{ wide: secondWide }"
        aria-label="Second guide panel"
      >
        <strong>Second guide panel</strong>
        <p>Review native keyboard interaction.</p>
      </section>
    </div>
    <output aria-live="polite">
      Last observed: {{ observedName }} · Content box: {{ Math.round(observedWidth) }} ×
      {{ Math.round(observedHeight) }} CSS pixels · Notifications: {{ notifications }}
    </output>
    <button type="button" :disabled="stopped || !isSupported" @click="stopObservation">
      Stop size observation
    </button>
    <p v-if="stopped">
      Observation stopped. Both panels can still resize; the last delivered measurement is retained.
    </p>
    <p v-else>
      Only the selected panel updates the readout. Changing the selected target detaches the
      previous observer.
    </p>
    <p v-if="!isSupported">Resize observation is not available in this context.</p>
  </div>
</template>

<style scoped>
.panel-size-choice {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  input {
    inline-size: auto;
  }
}
.guide-panels {
  display: grid;
  gap: 12px;
  margin-block: 12px;
}
.guide-panel {
  box-sizing: border-box;
  inline-size: 180px;
  max-inline-size: 100%;
  padding: 12px;
  border: 1px solid currentColor;
  border-radius: 8px;
}
.guide-panel.wide {
  inline-size: 280px;
}
</style>

Runtime contract

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

API

useResizeObserver

Observe size changes of one or more reactive elements. A single observer is (re)created whenever the resolved targets, the box option, or the host change; unresolved targets are skipped. The observer is disconnected when the owning reactive scope stops or stop is called. During server rendering no host resolves, isSupported stays false, and the callback never runs. Inside a component the host is read after mounting, so hydration renders the server fallback first and never mismatches.

function useResizeObserver( targets: MaybeElementTargets, callback: ResizeObserverCallback, options: UseResizeObserverOptions = {}, ): ResizeObserverControls

useElementSize

Track the rendered size of a reactive element. Reads the requested box from contentBoxSize/borderBoxSize (summing fragments) and falls back to contentRect for older engines. When the target unmounts the size resets to the initial value so stale geometry never leaks into the next render. Server renders expose initialSize.

function useElementSize( target: MaybeElementTarget, options: UseElementSizeOptions = {}, ): ElementSizeControls

Types

ResizeObserverHost

Runtime capability that supplies the ResizeObserver constructor.

Member Type Description
ResizeObserver? new (callback: ResizeObserverCallback) => ResizeObserver ResizeObserver constructor, absent in unsupported runtimes.

UseResizeObserverOptions

Options for useResizeObserver.

Member Type Description
box? ResizeObserverBoxOptions Box model reported by the observer.
host? MaybeRefOrGetter<ResizeObserverHost | null | undefined> Reactive constructor capability for alternate runtimes and tests.
flush? "pre" | "post" | "sync" Target resolution timing. "post" observes template refs after mount.

ResizeObserverControls

Controls returned by useResizeObserver.

Member Type Description
isSupported Readonly<Ref<boolean>> Whether the host provides ResizeObserver. false during server rendering.
stop () => void Disconnect the observer and stop following the reactive targets. Idempotent.

ElementSize

Element dimensions reported by useElementSize.

Member Type Description
width number Inline size in CSS pixels.
height number Block size in CSS pixels.

UseElementSizeOptions

Options for useElementSize.

Member Type Description
box? ResizeObserverBoxOptions Box model reported by the observer.
host? MaybeRefOrGetter<ResizeObserverHost | null | undefined> Reactive constructor capability for alternate runtimes and tests.
flush? "pre" | "post" | "sync" Target resolution timing. "post" observes template refs after mount.
initialSize? ElementSize Size exposed before the first observation and during server rendering. Keep it identical on server and client to avoid hydration mismatches.

ElementSizeControls

Reactive size returned by useElementSize.

Member Type Description
isSupported Readonly<Ref<boolean>> Whether the host provides ResizeObserver. false during server rendering.
stop () => void Disconnect the observer and stop following the reactive targets. Idempotent.
width Readonly<Ref<number>> Observed inline size in CSS pixels.
height Readonly<Ref<number>> Observed block size in CSS pixels.