npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-scrollobserver

v1.0.1

Published

Run code when an element scrolls into view in Vue 3, with a directive, a composable, or a plain function built on IntersectionObserver

Readme

vue-scrollobserver

Run code when an element scrolls into view in Vue 3, with a directive, a composable, or a plain function. 要素が画面に入ったときに処理を実行する、Vue 3 向けのディレクティブ、composable、関数です。

English

Important: vue-scrollobserver requires Vue 3.3 or later. It is the successor of @osaxyz/intersect, rewritten in TypeScript. v-intersect works as before, and only the package name and the plugin import change.

Quick start

  1. Install the package.
npm install vue-scrollobserver
  1. Register the plugin.
import { createApp } from "vue"
import { createIntersect } from "vue-scrollobserver"
import App from "./App.vue"

createApp(App).use(createIntersect()).mount("#app")
  1. Add v-intersect to an element.
<script setup lang="ts">
const onEnter = (entry: IntersectionObserverEntry) => {
    entry.target.classList.add("is-visible")
}
</script>

<template>
  <section v-intersect="onEnter">Runs every time it enters</section>
  <section v-intersect.once="onEnter">Runs only the first time</section>
</template>
  1. Scroll the page. The handler runs when the element enters the viewport.

Tip: Pass rootMargin: "0px 0px -20% 0px" to run the handler once the element is 20% of the screen above the bottom edge, instead of as soon as it appears.

To track whether an element is in view, use the composable. Outside components, use intersect().

import { useTemplateRef } from "vue"
import { intersect, useIntersect } from "vue-scrollobserver"

const target = useTemplateRef("target")
const { isIntersecting } = useIntersect(target)

const stop = intersect(el, (entry) => console.log(entry), { once: true })

Technology

vue-scrollobserver does not listen to scroll events or read layout on every frame. The browser reports when an element crosses the viewport, so pages with many triggers stay smooth. Each element gets its own observer, which is disconnected when the element unmounts or when once has run.

Nothing is observed on the server. isIntersecting from useIntersect() starts as false, so the server render and hydration agree, and the directive renders no attributes on the server. Where IntersectionObserver does not exist, every entry point does nothing.

Releases are built and published from GitHub Actions with npm Trusted Publishing, so no npm token exists anywhere. Each version carries a provenance statement that links it to the commit and workflow run it was built from. The build job and the job that holds the publishing credential are separate, so a compromised dependency cannot publish a fake package. The workflow only stages each version, and it reaches users after a maintainer approves it with two-factor authentication.

Specification

| Usage | Behavior | | --- | --- | | v-intersect="handler" | Runs handler(entry, el) each time the element enters | | v-intersect.once="handler" | Runs once, then stops observing | | v-intersect:[selector]="handler" | Observes against the element matching the selector instead of the viewport | | v-intersect="{ handler, ...options }" | Passes options for this element | | v-intersect="false" | Does nothing |

The plugin registers the directive with the options you pass. Without the plugin, import vIntersect in <script setup>. createIntersectDirective(options) builds a directive with its own defaults.

| Export | Description | | --- | --- | | intersect(el, handler, options?) | Runs handler when el enters, and returns a function that stops observing. Can be called anywhere | | isIntersectSupported() | Whether the browser has IntersectionObserver. false during SSR | | useIntersect(target, handler?, options?) | Returns a readonly isIntersecting ref and stop(). target can be a ref, a getter or an element, and a new element is observed when it changes | | createIntersect(options?) | Plugin that registers the directive and sets app-wide defaults | | createIntersectDirective(options?) | Builds a directive with its own defaults |

| Option | Type | Default | Description | | --- | --- | --- | --- | | root | Element \| Document \| string \| null | null | Element to observe against. A string is a selector, resolved when observing starts. null is the viewport | | rootMargin | string | "0px" | Grows or shrinks the root, in the same shape as CSS margin | | threshold | number \| number[] | 0.01 | How much of the element must be visible | | once | boolean | false | Stops observing after the handler runs once | | disabled | MaybeRefOrGetter<boolean> | false | Skips the handler while true. Skipped entries do not use up once | | directive | string \| false | "intersect" | createIntersect only. Name of the directive. false skips registration |

Options given to the directive or to useIntersect() take precedence over the plugin options.

Register the plugin in a file under plugins/.

import { createIntersect } from "vue-scrollobserver"

export default defineNuxtPlugin((nuxtApp) => {
    nuxtApp.vueApp.use(createIntersect())
})

| @osaxyz/intersect | vue-scrollobserver | | --- | --- | | import intersectDirective from "@osaxyz/intersect" | import { createIntersect } from "vue-scrollobserver" | | app.use(intersectDirective) | app.use(createIntersect()) |

Templates need no changes. v-intersect, its argument, the (entry, el) handler and the defaults are the same.

日本語

重要:vue-scrollobserver には Vue 3.3 以上が必要です。@osaxyz/intersect の後継として TypeScript で書き直したものです。v-intersect はそのまま使え、変わるのはパッケージ名とプラグインの import だけです。

クイックスタート

  1. パッケージをインストールします。
npm install vue-scrollobserver
  1. プラグインを登録します。
import { createApp } from "vue"
import { createIntersect } from "vue-scrollobserver"
import App from "./App.vue"

createApp(App).use(createIntersect()).mount("#app")
  1. 要素に v-intersect を付けます。
<script setup lang="ts">
const onEnter = (entry: IntersectionObserverEntry) => {
    entry.target.classList.add("is-visible")
}
</script>

<template>
  <section v-intersect="onEnter">画面に入るたびに実行します</section>
  <section v-intersect.once="onEnter">最初の1回だけ実行します</section>
</template>
  1. ページをスクロールします。要素が画面に入るとハンドラが実行されます。

ヒント:rootMargin: "0px 0px -20% 0px" を渡すと、要素が見えた瞬間ではなく、画面の下端から 20% 上まで来たときに実行します。

要素が画面にあるかを追うときは composable を使います。コンポーネントの外では intersect() を使います。

import { useTemplateRef } from "vue"
import { intersect, useIntersect } from "vue-scrollobserver"

const target = useTemplateRef("target")
const { isIntersecting } = useIntersect(target)

const stop = intersect(el, (entry) => console.log(entry), { once: true })

テクノロジー

scroll イベントを監視したり、フレームごとにレイアウトを読んだりしません。要素が画面の境界を越えたときにブラウザが知らせるので、トリガーが多いページでも滑らかに動きます。監視は要素ごとに1つで、要素がアンマウントされたときと once で実行したあとに切断します。

サーバーでは何も監視しません。useIntersect() の isIntersecting は false で始まるので、サーバーの描画とハイドレーションの結果が一致します。ディレクティブはサーバーでは属性を出力しません。IntersectionObserver がない環境では、どの入口も何もしません。

リリースは GitHub Actions から npm の Trusted Publishing でビルドして公開するので、npm のトークンはどこにも存在しません。各バージョンには、どのコミットとワークフローの実行からビルドしたかを示す provenance が付きます。ビルドするジョブと公開の証明書を持つジョブを分けているので、依存のどれかが乗っ取られても偽のパッケージは公開できません。ワークフローは各バージョンを段階公開するだけで、メンテナーが 2 要素認証を使って承認してから利用者に届きます。

仕様

| 書き方 | 動作 | | --- | --- | | v-intersect="handler" | 要素が画面に入るたびに handler(entry, el) を実行します | | v-intersect.once="handler" | 1回だけ実行し、監視をやめます | | v-intersect:[selector]="handler" | 画面の代わりに、セレクタに一致する要素を基準にします | | v-intersect="{ handler, ...options }" | この要素だけのオプションを渡します | | v-intersect="false" | 何もしません |

プラグインは、渡したオプションでディレクティブを登録します。プラグインを使わない場合は、<script setup> で vIntersect を import して使います。createIntersectDirective(options) で、独自の既定値を持つディレクティブを作れます。

| export | 説明 | | --- | --- | | intersect(el, handler, options?) | el が画面に入ったら handler を実行し、監視をやめる関数を返します。どこからでも呼べます | | isIntersectSupported() | ブラウザに IntersectionObserver があるかを返します。SSR 中は false です | | useIntersect(target, handler?, options?) | 読み取り専用の ref の isIntersecting と stop() を返します。target には ref、getter、要素を渡せ、変わると新しい要素を監視します | | createIntersect(options?) | ディレクティブを登録し、アプリ全体の既定値を設定するプラグインです | | createIntersectDirective(options?) | 独自の既定値を持つディレクティブを作ります |

| オプション | 型 | 既定値 | 説明 | | --- | --- | --- | --- | | root | Element \| Document \| string \| null | null | 基準にする要素です。文字列はセレクタとして、監視を始めるときに探します。null は画面です | | rootMargin | string | "0px" | 基準の範囲を広げたり狭めたりします。書き方は CSS の margin と同じです | | threshold | number \| number[] | 0.01 | 要素のどれだけが見えたら実行するかです | | once | boolean | false | ハンドラを1回実行したら監視をやめます | | disabled | MaybeRefOrGetter<boolean> | false | true の間はハンドラを実行しません。実行しなかった回は once の1回に数えません | | directive | string \| false | "intersect" | createIntersect だけで使います。ディレクティブの名前で、false なら登録しません |

ディレクティブや useIntersect() に渡したオプションは、プラグインのオプションより優先します。

plugins/ の下のファイルでプラグインを登録します。

import { createIntersect } from "vue-scrollobserver"

export default defineNuxtPlugin((nuxtApp) => {
    nuxtApp.vueApp.use(createIntersect())
})

| @osaxyz/intersect | vue-scrollobserver | | --- | --- | | import intersectDirective from "@osaxyz/intersect" | import { createIntersect } from "vue-scrollobserver" | | app.use(intersectDirective) | app.use(createIntersect()) |

テンプレートは書き換えなくて済みます。v-intersect、その引数、(entry, el) を受け取るハンドラ、既定値は変わりません。