A composable for detecting when elements enter or leave the viewport using the Intersection Observer API with automatic cleanup.
The useIntersectionObserver
composable wraps the Intersection Observer API to detect when elements become visible in the viewport. It’s useful for lazy loading images, infinite scroll, entrance animations, and performance optimizations.
<script setup>
import { useIntersectionObserver } from '@vuetify/v0'
import { ref, useTemplateRef } from 'vue'
const target = useTemplateRef('target')
const isVisible = ref(false)
useIntersectionObserver(target, (entries) => {
isVisible.value = entries[0].isIntersecting
}, {
threshold: 0.5, // Trigger when 50% visible
rootMargin: '0px'
})
</script>
<template>
<div>
<div style="height: 100vh">Scroll down to see the element</div>
<div ref="target" :class="{ visible: isVisible }">
I'm {{ isVisible ? 'visible' : 'hidden' }}
</div>
</div>
</template>
Composable | Description |
---|---|
useResizeObserver→ | Observe element size changes |
useMutationObserver→ | Observe DOM mutations |
useEventListener→ | General event handling |
useIntersectionObserver
Type
interface IntersectionObserverOptions {
immediate?: boolean
root?: Element | null
rootMargin?: string
threshold?: number | number[]
}
function useIntersectionObserver(
target: Ref<Element | undefined>,
callback: (entries: IntersectionObserverEntry[]) => void,
options?: IntersectionObserverOptions
): {
isIntersecting: Readonly<Ref<boolean>>
isPaused: Readonly<Ref<boolean>>
pause: () => void
resume: () => void
stop: () => void
}
Details
Observes when an element intersects with the viewport or a specified ancestor element. Automatically handles cleanup on component unmount.
Parameters
target
: Ref to the element to observecallback
: Function called when intersection changesoptions
: immediate
: Trigger callback immediately with synthetic entry (default: false)root
: The element used as viewport for checking visibility (default: null - uses viewport)rootMargin
: Margin around the root, e.g., ‘10px 20px 30px 40px’ (default: ‘0px’)threshold
: Visibility percentage(s) that trigger the callback, 0-1 or array (default: 0)Returns
isIntersecting
: Whether the element is currently intersectingisPaused
: Whether observation is pausedpause()
: Pause observationresume()
: Resume observationstop()
: Stop observation permanentlyExample
const element = useTemplateRef('element')
const { isIntersecting, pause, resume } = useIntersectionObserver(
element,
([entry]) => {
console.log('Intersection ratio:', entry.intersectionRatio)
},
{
threshold: [0, 0.5, 1], // Trigger at 0%, 50%, and 100%
rootMargin: '100px' // Start observing 100px before entering
}
)
useElementIntersection
Type
function useElementIntersection(
target: Ref<Element | undefined>,
options?: IntersectionObserverOptions
): {
isIntersecting: Readonly<Ref<boolean>>
intersectionRatio: Readonly<Ref<number>>
isPaused: Readonly<Ref<boolean>>
pause: () => void
resume: () => void
stop: () => void
}
Details
Convenience function for tracking element intersection state reactively without a callback.
Returns
isIntersecting
: Whether the element is currently intersectingintersectionRatio
: How much of the element is visible (0-1)isPaused
: Whether observation is pausedpause()
: Pause observationresume()
: Resume observationstop()
: Stop observation permanentlyExample
const image = useTemplateRef('image')
const { isIntersecting } = useElementIntersection(image, {
threshold: 0.1
})
// Use isIntersecting in template or watch
watch(isIntersecting, (visible) => {
if (visible) loadImage()
})