vue-waypoint
| Install | |
|---|---|
npm install vue-waypoint |
|
| Latest Version: | 5.1.0 |
| Vue: | 3 |
| Older releases: | 2 |
| License: | MIT |
| Last Updated: | Oct 13, 2026 |
| Links: | GitHub · npm |
vue-waypoint
Trigger functions and events based on an element's position on screen — a tiny, dependency-free Vue 3 wrapper around the native
IntersectionObserver.
Demo
Live demo: scroll up and down and watch the on-page @change log, the CSS helper classes and the slot state update.
Features
- Vue 3.3+, written in TypeScript with shipped type declarations
- Zero runtime dependencies — a thin wrapper over the native
IntersectionObserver - ESM + CJS builds (tree-shakable) with correct
exportsand matching.d.ts/.d.ctstypes - Flexible: custom tag, custom observer options, reactive
activetoggle - Optional CSS helper classes for quick, configuration-free animations
- Slot support exposing the live waypoint state
useWaypointcomposable for observing any template ref directly- SSR-safe
Install
npm i vue-waypoint
Usage
<script setup> (recommended)
<script setup lang="ts">
import { Waypoint, type WaypointState } from "vue-waypoint";
function onChange(state: WaypointState) {
// state.going -> "IN" | "OUT"
// state.direction -> "UP" | "DOWN" | "LEFT" | "RIGHT"
// state.el -> the observed Element
console.log(state.going, state.direction);
}
</script>
<template>
<Waypoint @change="onChange">
<!-- anything you want here -->
</Waypoint>
</template>
Options API
<script lang="ts">
import { defineComponent } from "vue";
import { Waypoint, type WaypointState } from "vue-waypoint";
export default defineComponent({
components: { Waypoint },
setup() {
const onChange = (state: WaypointState) => {
console.log(state.going, state.direction);
};
return { onChange };
},
});
</script>
<template>
<Waypoint @change="onChange" />
</template>
Props
active
Reactively enable or disable the waypoint. The element is observed while active is true and unobserved when it flips to false.
- Enable:
<Waypoint :active="true" /> - Disable:
<Waypoint :active="false" />
options
A standard IntersectionObserverInit object, forwarded verbatim to the underlying observer.
const options: IntersectionObserverInit = {
root: document,
rootMargin: "0px 0px 0px 0px",
threshold: [0.25, 0.75],
};
<Waypoint :options="options" />
The observer is rebuilt only when the options actually change value (root by identity, threshold as a number or an array, the rest by value), so an inline literal such as :options="{ threshold: 0.5 }" is fine: parent re-renders do not recreate the observer.
tag
The rendered element tag. Defaults to div.
<Waypoint tag="div" />→<div class="waypoint"></div><Waypoint tag="span" />→<span class="waypoint"></span><Waypoint tag="p" />→<p class="waypoint"></p>
disableCssHelpers
Disable the automatic CSS helper classes. Defaults to false.
- With helpers (default):
<Waypoint />→<div class="waypoint going-in direction-down"></div> - Without helpers:
<Waypoint :disable-css-helpers="true" />→<div></div>
CSS helpers
Zero configuration, handy for simple CSS animations. The component toggles three families of classes:
waypoint— set as soon as the waypoint is readygoing-in/going-out— toggled as the element enters and leaves the viewportdirection-up/direction-down/direction-left/direction-right— toggled as the scroll direction changes
Examples:
waypoint going-in direction-up— visible, came from the bottom, scrolling up (natural scroll)waypoint going-in direction-down— visible, came from the top, scrolling downwaypoint going-out direction-up— hidden, was scrolling upwaypoint going-out direction-down— hidden, was scrolling down
Events
change
Emitted every time the waypoint detects an intersection change.
interface WaypointState {
el: Element | undefined;
going: "IN" | "OUT" | undefined;
direction: "UP" | "DOWN" | "LEFT" | "RIGHT" | undefined;
}
<template>
<Waypoint @change="onChange" />
</template>
The same WaypointState is also exposed through the default slot:
<Waypoint #default="{ going, direction }">
<span v-if="going">going-{{ going.toLowerCase() }}</span>
<span v-if="direction">direction-{{ direction.toLowerCase() }}</span>
</Waypoint>
Composable
Prefer composing over a component? useWaypoint observes a template ref directly, with the same reactive active/options behavior as <Waypoint>:
<script setup lang="ts">
import { ref } from "vue";
import { useWaypoint } from "vue-waypoint";
const target = ref<Element | null>(null);
const { state } = useWaypoint(target, {
active: true,
observerOptions: { threshold: 0.5 },
});
</script>
<template>
<div ref="target">{{ state?.going }}</div>
</template>
active and observerOptions accept a plain value or a Ref. When the target ref points to a new element (for example after a v-if / v-else swap), observation moves to the new element, and setting it to null stops observing the old one. The observer is created lazily (only once the target element and active are both truthy) and is disconnected automatically when the enclosing component scope is torn down. Outside a component scope, call the returned stop() yourself.
Development
npm i # install
npm run dev # run the demo app
npm run lint # eslint (flat config)
npm run type-check
npm test # vitest
npm run build # type-check + library build (ESM + CJS + .d.ts/.d.cts)
npm run lint:package # publint + arethetypeswrong on the packed tarball
Legacy: Vue 2 and Nuxt
The Vue 2 line (and its Nuxt 2 notes) lives on the vue2 branch. The 4.x/5.x releases target Vue 3 only. They are SSR-safe, so they also work in Nuxt 3+: import Waypoint or useWaypoint from vue-waypoint in your components as usual.
License
Related Packages
Reveal-on-scroll animations for Vue, built on the Web Animations API and Interse...
Vue.js directives for viewport detection - v-inview and v-outview directives pow...
Lightweight JavaScript library for viewport detection with debounced callbacks -...
Named, reactive, matchMedia-based viewport breakpoints for Vue 3 — a tiny plugin...
Version History
| Version | Released | License |
|---|---|---|
| 5.1.0 | MIT | |
| 5.0.3 | MIT | |
| 5.0.2 | MIT | |
| 5.0.1 | MIT | |
| 5.0.0 | MIT | |
| 4.3.0 | MIT | |
| 4.2.5 | MIT | |
| 4.2.4 | MIT | |
| 4.2.3 | MIT | |
| 4.2.1 | MIT | |
| 4.2.0 | MIT | |
| 4.1.1 | MIT | |
| 4.1.0 | MIT | |
| 4.0.0 | MIT | |
| 3.5.2 | MIT | |
| 3.5.1 | MIT | |
| 3.5.0 | MIT | |
| 3.4.1 | MIT | |
| 3.2.2 | MIT | |
| 3.1.0 | MIT | |
| 3.0.3 | MIT | |
| 3.0.2 | MIT | |
| 3.0.1 | MIT | |
| 3.0.0 | MIT | |
| 2.5.0 | MIT | |
| 2.4.0 | MIT | |
| 2.3.0 | MIT | |
| 2.2.2 | MIT | |
| 2.2.1 | MIT | |
| 2.2.0 | MIT |
Showing the latest 30 of 52. See every release on npm