Vize

element-bounding

Track an element's getBoundingClientRect() reactively.

Package @vizejs/composable/element-bounding
Own the source vize lib pull composable:element-bounding
Runtime exports useElementBounding
Gzip budget 3840 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 { useElementBounding } from "@vizejs/composable/element-bounding";

Try it

Measure a guide panel's actual border rectangle

Observe actual viewport-relative geometry, explicitly refresh a transformed position, and distinguish missing or stopped targets from current measurements.

Open the interactive example

What to observe: Toggle Wide guide panel and compare its actual border-box size. Shift guide panel changes CSS position without resizing; Measure current rectangle explicitly refreshes that position. Hide the panel to reset all values to zero, then show it again. Stop rectangle observation freezes the labelled last-recorded rectangle while all panel checkboxes keep working.

useElementBounding exposes readonly refs from the actual getBoundingClientRect, including border and padding, relative to this preview's viewport. Template resolution, native ResizeObserver delivery, window resize, and captured ancestor scrolling refresh the geometry. A transform does not resize the target, so the example offers the public update action explicitly. Default reset clears all values when the target disappears. stop removes automatic observation; update remains a callable API, but this example disables its button to retain an explicitly last-recorded snapshot. SSR values are zero and no target geometry is read. Scope disposal removes its observer, target watcher, and listeners. No geometry host is simulated.

Compare the initial and interacted states
Measure a guide panel's actual border rectangle: initial state
Initial state
Measure a guide panel's actual border rectangle: 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, useTemplateRef } from "vue";
import { useElementBounding } from "@vizejs/composable/element-bounding";

const panel = useTemplateRef<HTMLElement>("panel");
const visible = ref(true);
const wide = ref(false);
const shifted = ref(false);
const stopped = ref(false);
const { left, top, width, height, update, stop } = useElementBounding(panel);

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

<template>
  <div class="composable-example">
    <div class="measurement-controls">
      <label><input v-model="visible" type="checkbox" /> Show guide panel</label>
      <label><input v-model="wide" type="checkbox" /> Wide guide panel</label>
      <label><input v-model="shifted" type="checkbox" /> Shift guide panel</label>
    </div>
    <section
      v-if="visible"
      ref="panel"
      class="measurement-panel"
      :class="{ wide, shifted }"
      aria-label="Measured guide panel"
    >
      <strong>Review the actual panel</strong>
      <p>Geometry belongs to this viewport.</p>
    </section>
    <div class="example-actions">
      <button type="button" :disabled="stopped" @click="() => update()">
        Measure current rectangle
      </button>
      <button type="button" :disabled="stopped" @click="stopObservation">
        Stop rectangle observation
      </button>
    </div>
    <output aria-live="polite">
      {{ stopped ? "Last recorded" : "Measured" }} rectangle: left {{ Math.round(left) }}, top
      {{ Math.round(top) }}, width {{ Math.round(width) }}, height {{ Math.round(height) }} CSS
      pixels
    </output>
    <p>
      Size and scroll changes refresh the native border rectangle automatically. A CSS transform
      changes position without resizing: use Measure current rectangle to refresh it explicitly.
      Hiding the observed panel resets its measurements to zero while observation is active.
    </p>
    <p v-if="stopped">
      Observation stopped. Native panel controls still work; the last recorded rectangle stays
      fixed.
    </p>
  </div>
</template>

<style scoped>
.measurement-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;

  label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
  }

  input {
    inline-size: auto;
  }
}
.measurement-panel {
  box-sizing: border-box;
  inline-size: 160px;
  max-inline-size: calc(100% - 40px);
  padding: 12px;
  border: 1px solid currentColor;
  border-radius: 8px;

  &.wide {
    inline-size: 230px;
  }

  &.shifted {
    transform: translateX(40px);
  }
}
</style>

Runtime contract

Utility Category Stability SSR Hydration Cleanup Targets Host globals Uses
useElementBounding dom experimental deterministic-fallback stable caller, reactive-scope web, desktop window resolveElement, useEventListener, useResizeObserver, tryOnScopeDispose

API

useElementBounding

Track an element's getBoundingClientRect() reactively. Measures when the target resolves, when it resizes (via ResizeObserver), and — unless disabled — when the window resizes or anything scrolls. Every value is 0 during server rendering and before the first measurement, so the initial render is hydration-stable. Listeners and the observer are released with the owning reactive scope.

function useElementBounding( target: MaybeElementTarget, options: UseElementBoundingOptions = {}, ): ElementBoundingControls

Types

ElementBoundingHost

Window-like capability observed by useElementBounding: scroll and resize events plus the optional ResizeObserver constructor.

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

ElementBoundingRect

Viewport-relative element geometry in CSS pixels.

Member Type Description
x number Horizontal position of the border box origin.
y number Vertical position of the border box origin.
top number Distance from the viewport top edge to the border box top edge.
right number Distance from the viewport left edge to the border box right edge.
bottom number Distance from the viewport top edge to the border box bottom edge.
left number Distance from the viewport left edge to the border box left edge.
width number Border box width.
height number Border box height.

UseElementBoundingOptions

Options for useElementBounding.

Member Type Description
reset? boolean Reset every value to zero when the target unmounts.
windowResize? boolean Re-measure when the window resizes.
windowScroll? boolean Re-measure when any ancestor (or the window) scrolls. Uses a capturing, passive listener so nested scroll containers are covered.
host? MaybeRefOrGetter<ElementBoundingHost | null | undefined> Reactive window capability for alternate runtimes and tests.
flush? "pre" | "post" | "sync" Target resolution timing. "post" measures template refs after mount.