Skip to content

v-reveal Directive

v-reveal — toggles a class (default "in") and/or a data-attribute on the bound element as it enters the viewport, without calling useElementVisibility() yourself. A directive is DOM-level, not bound to setup() the way a composable is, so it works directly inside v-for — including items rendered after an async fetch, where a composable can't be called per iteration.

vue
<template>
  <div v-for="item in items" :key="item.id" v-reveal.once class="card">
    {{ item.title }}
  </div>
</template>

v-reveal.once is shorthand for v-reveal="{ once: true }". Pass an object for the rest:

vue
<div v-reveal="{ once: true, threshold: 0.3, class: 'visible', onEnter: (info) => track(item.id) }">

A fresh inline options object on every re-render — the common case, since v-reveal="{ once: true, onEnter: ... }" builds a new object literal each time — doesn't force a re-subscribe: class/attribute/once/ threshold/rootMargin/root are compared by value (a threshold array by its joined contents, not by reference), and onEnter/onLeave identity is ignored entirely. The directive only unbinds and rebinds when one of those values actually changed.

Binding value

class

string | null · default: 'in'

Pass null to disable the class toggle entirely (e.g. if you only want attribute).

attribute

string | null · default: null

A boolean data-attribute set alongside class (present while intersecting, removed otherwise) — for styling purely by attribute selector.

once

boolean · default: viewportDefaults.once, or true if the .once modifier is present

threshold

number | number[] · default: viewportDefaults.threshold

rootMargin

string · default: viewportDefaults.rootMargin

root

HTMLElement | null · default: null

onEnter / onLeave

(info: IntersectionInfo) => void · default: —

Same IntersectionInfo shape as Visibility Engine.

All of the above fall back to viewportDefaults the same way useElementVisibility does — set them once with setViewportDefaults() instead of repeating options on every v-reveal.

Modifiers

.once

Shorthand for once: true — equivalent to v-reveal="{ once: true }", but an explicit once in the object value always wins if both are given.

Example:

vue
<script setup lang="ts">
const cards = [{ id: 1, title: 'A' } /* ...rendered by a v-for, possibly after a fetch */]
</script>

<template>
  <div
    v-for="card in cards"
    :key="card.id"
    v-reveal="{ once: true, threshold: 0.2 }"
    class="reveal"
  >
    {{ card.title }}
  </div>
</template>
css
.reveal {
  opacity: 0;
  transition: opacity 0.3s;
}
.reveal.in {
  opacity: 1;
}

When you'd reach for this instead

  • A single, already-known element — plain useElementVisibility() is simpler when there's no v-for involved.
  • The reactive isVisible value needs to drive more than a class (e.g. conditionally mounting a heavy child) — see <InView>, which exposes it via a scoped slot.
  • A whole page (or a container) of markup you don't want to annotate with a directive/composable at all — see Reveal Controller, a single framework-agnostic call that finds matching elements on its own, including ones added later.