InView Component
<InView> — a renderless wrapper around useElementVisibility for v-for items that need the reactive isVisible/ratio value back in the template (e.g. to lazily mount a heavy child), not just a class toggle — see v-reveal for the class-toggle-only case without the extra component.
Always renders one real wrapping element (as, default 'div') — an IntersectionObserver needs an actual element to measure, so "renderless" here means no imposed styling/behavior beyond that, not zero DOM.
<template>
<InView v-for="item in items" :key="item.id" once v-slot="{ isVisible }">
<HeavyChart v-if="isVisible" :item="item" />
</InView>
</template>Props
once
boolean · default: viewportDefaults.once
threshold
number | number[] · default: viewportDefaults.threshold
rootMargin
string · default: viewportDefaults.rootMargin
root
HTMLElement | null · default: null
as
string · default: 'div'
Tag for the wrapping element visibility is actually measured on.
Slots
default
{ isVisible: boolean, ratio: number }
Same shape as useElementVisibility's return value (as plain values, not refs — the slot re-renders on every change).
Events
enter / leave
(info: IntersectionInfo) => void
Same IntersectionInfo shape as Visibility Engine — the component's equivalent of useElementVisibility's onEnter/onLeave options.
Example:
<script setup lang="ts">
import { InView } from '@macrulez/inview-vue'
</script>
<template>
<InView as="section" :threshold="0.3" @enter="onCardSeen">
<template #default="{ isVisible, ratio }">
<div :class="{ 'is-visible': isVisible }">{{ Math.round(ratio * 100) }}% visible</div>
</template>
</InView>
</template>threshold/rootMargin/once fall back to viewportDefaults when omitted, same as useElementVisibility and v-reveal.