Skip to content

Element Viewport Position ​

useElementViewport(target, options?) — reactive position of an element relative to the viewport: its rect, viewportProgress, and distanceFromCenter, updated on the shared requestAnimationFrame loop. Pass root to measure against a scrollable container instead of the window.

ts
function useElementViewport(
  target: MaybeRefOrGetter<HTMLElement | null | undefined>,
  options?: UseElementViewportOptions,
): UseElementViewportReturn

interface UseElementViewportOptions {
  root?: MaybeRefOrGetter<HTMLElement | null | undefined> // default: null (the window)
}

A thin reactive wrapper over createElementTracker() — see that page for exactly what each field means and how it's computed, including how root changes the viewportProgress/ distanceFromCenter frame while rect stays window-relative. Changing target or root re-subscribes.

Return value ​

rect ​

Ref<DOMRectLike>

Default (before target resolves, or during SSR): { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 }.

viewportProgress ​

Ref<number>

0..1, from the element entering at the viewport's bottom edge to leaving at its top edge.

distanceFromCenter ​

Ref<number>

Example:

vue
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useElementViewport } from '@macrulez/inview-vue'

const section = ref<HTMLElement | null>(null)
const { viewportProgress } = useElementViewport(section)
const opacity = computed(() => Math.min(1, viewportProgress.value * 2))
</script>

<template>
  <section ref="section" :style="{ opacity }">...</section>
</template>

Tracking inside a scrollable panel instead of the window:

vue
<script setup lang="ts">
const panel = ref<HTMLElement | null>(null)
const widget = ref<HTMLElement | null>(null)
const { viewportProgress } = useElementViewport(widget, { root: panel })
</script>

For a ready-made parallax effect built on this same data, see Parallax Layer.