Skip to page content

useElementSize composable
v2.34+

The useElementSize() composable tracks the size of an element (or of a component) through a reactive elementSize Object holding its width and height. Under the hood it uses the Resize Observer API, so no polling is involved.

It is the setup-code counterpart of the QResizeObserver component and of the v-resize directive, which are both built on it. Use the composable when you want the size on your component or on any element or component ref, without adding an extra node to your template.

The reported size is the element’s outer size (padding and border included), so a padding change on the element itself is reported too.

NOTE

On the server-side of SSR or SSG modes, the composable never measures anything: elementSize stays at { width: 0, height: 0 } until the client takes over.

Outside of a component

The composable can also be called outside of setup(): in a boot file, a store or a plain module. There is no component root to fall back on and no mount to wait for, so supply a target (an element, or a ref or getter of one); the observation starts right away and nothing stops it by itself: call stopElementSize() when you are done.

Syntax

import { useTemplateRef } from 'vue'
import { useElementSize } from 'quasar'

setup () {
  const target = useTemplateRef('target') // an Element or a component

  const { elementSize, refreshElementSize, stopElementSize } = useElementSize({
    // all optional:
    target,                  // (default: the component's own root element)
    debounce: 100,           // ms; at most one measurement per window (default: 0, one per change)
    disabled: true,          // pause observing (default: false)
    onResize (elementSize) { // called with { width, height } on every change
      // ...
    }
  })

  // ...
}
function useElementSize(
  options?: MaybeRefOrGetter<{
    target?: MaybeRefOrGetter<
      Element | ComponentPublicInstance | null | undefined
    >
    debounce?: string | number
    disabled?: boolean
    onResize?: (elementSize: { width: number; height: number }) => void
  }>
): {
  elementSize: ShallowRef<{ width: number; height: number }>
  refreshElementSize: () => void
  stopElementSize: () => void
}

Without a target, the composable measures the root element of the component it is called in, as of the moment the component gets mounted. A component rendering a fragment (multiple root nodes) has no root element to measure, so supply a target there.

The first measurement happens as soon as the element is available, so elementSize holds the size right after your component gets mounted; onResize is called for it as well, then only when the size changes. Each change assigns a new Object to elementSize, so a watcher on the ref fires once per reported size.

With a debounce, the element gets measured at most once per window of that many milliseconds: a continuous resize (dragging a splitter, animating a width) reports periodically rather than on every frame, and the last change is never missed.

refreshElementSize() measures the element right away, skipping the debounce (it does nothing while disabled or before the target exists). You will rarely need it, since the browser reports every change on its own.

stopElementSize() ends the observation for good. You will rarely need it either, as the composable stops by itself when the component gets destroyed.

Changing the options while running

The options can be a plain Object, a Ref or a getter Function. A plain Object is read once. With a Ref or a getter, the composable tracks whatever reactive state the options read and re-applies them whenever that state changes, so you never call anything to “update” it:

  • toggling disabled pauses and resumes the observation (elementSize keeps its last value while paused, and resuming measures right away)
  • pointing target to another element (or letting a template ref change through v-if) follows it and reports the new element’s size
  • changing debounce applies from the next change
  • swapping onResize takes effect from the next change
import { ref } from 'vue'
import { useElementSize } from 'quasar'

setup () {
  const paused = ref(false)

  const { elementSize } = useElementSize(() => ({
    disabled: paused.value,
    debounce: paused.value ? 0 : 100
  }))

  function pause () { paused.value = true }
  function resume () { paused.value = false }

  // ...
}

Example

The box below is measured through a template ref: elementSize follows it, and the onResize hook counts how many times the size got reported:

To measure the component’s own root element instead, omit the target:

<template>
  <div class="chart">
    <svg
      :viewBox="`0 0 ${elementSize.width} ${elementSize.height}`"
      :width="elementSize.width"
      :height="elementSize.height"
    >
      <!-- ... -->
    </svg>
  </div>
</template>

<script setup>
  import { useElementSize } from 'quasar'

  // measures this component's root element (the chart container)
  const { elementSize } = useElementSize({ debounce: 100 })
</script>