Skip to content

Repository files navigation

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.

npm version npm downloads CI minzipped size license

Demo

Simple demo page — open your browser console and watch the events fire while scrolling up and down.

Features

  • Vue 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 exports
  • Flexible: custom tag, custom observer options, reactive active toggle
  • Optional CSS helper classes for quick, configuration-free animations
  • Slot support exposing the live waypoint state
  • useWaypoint composable 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" />

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 ready
  • going-in / going-out — toggled as the element enters and leaves the viewport
  • direction-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 down
  • waypoint going-out direction-up — hidden, was scrolling up
  • waypoint 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. 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)

Legacy: Vue 2 and Nuxt

The Vue 2 line lives on the vue2 branch. The 4.x/5.x releases target Vue 3 only.

License

MIT

About

Vue 3 waypoint component — trigger functions and events when an element enters/leaves the viewport, via IntersectionObserver. TypeScript, SSR-safe.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

461 stars

Watchers

7 watching

Forks

Releases

Used by

Contributors

Languages