Skip to content

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.

vue
<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:

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