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.
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


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. |