Skip to content

InView ​

Framework-agnostic engines for scroll, element visibility, and viewport-relative position — with adapters for Vue 3, Nuxt, and React sharing one requestAnimationFrame loop and a pooled IntersectionObserver.

Features ​

  • Scroll tracking — position, direction, progress, and velocity for the window or a scrollable element, all derived on one shared rAF loop instead of a separate loop per subscriber.
  • Visibility tracking — reactive IntersectionObserver-backed visibility with enter/leave edge detection and a once mode. Observers are pooled by (root, rootMargin, threshold), so many elements with the same options share one native observer.
  • Element viewport position — an element's bounding rect, how far it's traveled through the viewport (viewportProgress, 0..1), and its distance from the viewport's center — the geometry a reveal effect or progress indicator actually needs, already computed. Measurable against a scrollable container instead of the window via a root option.
  • A parallax layer primitive — turns viewportProgress into a CSS transform with a configurable speed, axis, edge clamping, and easing, and disables itself under prefers-reduced-motion automatically.
  • Scroll spy — createScrollSpy tracks which of a list of targets currently sits in a band near the viewport's center and reports its index, the standard nav-highlight idiom (a docs sidebar, a table of contents), sharing the same pooled observer as everything else.
  • SSR-safe everywhere — every engine and composable/hook returns a static no-op snapshot when there's no window, and starts a real subscription once it's running on the client — no <ClientOnly> wrapping required.
  • Vue, Nuxt, and React adapters over one core — useScroll, useElementVisibility, useElementViewport, and useParallaxLayer exist as Vue composables, auto-imported Nuxt composables, and React hooks with the same API and behavior — all three built on the same framework-agnostic engines underneath.
  • Reveal at scale, without per-element wiring — createRevealController scans the DOM for a selector (default [data-reveal], .reveal) and toggles a class/attribute as elements enter the viewport, including ones added later, picked up via a MutationObserver. Every option can be overridden per element via a data-reveal-* attribute, and observe()/unobserve() add elements directly without relying on the selector scan. Vue and React both get a useRevealController lifecycle wrapper; Vue additionally gets a v-reveal directive and an <InView> renderless component, React its own <InView> render-prop component, useReveal hook, and an <InviewProvider> for app-level defaults. staggerDelay computes a CSS delay for staggered groups, with an optional repeating "cycle" mode for long lists, and is skipped entirely under prefers-reduced-motion.
  • Small reactive utilities — useScrollTo/scrollToElement smooth-scroll a container so an element lands at its edge with an offset; usePrefersReducedMotion turns the static prefers-reduced-motion check into a live value that updates on the media query's change event; useStagger is a reactive wrapper around staggerDelay.
  • Every adapter re-exports the full core — @macrulez/inview-vue and @macrulez/inview-react re-export everything from @macrulez/inview-core, and @macrulez/inview-nuxt auto-imports it too, so reaching a lower-level engine like createRevealController never needs a separate @macrulez/inview-core install.

How It Works ​

The package is four npm packages sharing one architecture: a framework-agnostic core (@macrulez/inview-core) does the actual work — scroll tracking, IntersectionObserver pooling, rAF batching — with no framework dependency at all. @macrulez/inview-vue wraps each core engine in a reactive composable (Refs in, Refs out). @macrulez/inview-react wraps the same core engines in hooks built on useSyncExternalStore, mirroring the Vue composables' API 1:1 but with plain values instead of refs. @macrulez/inview-nuxt is a thin Nuxt module on top of the Vue package: it auto-imports every composable, utility, and core engine, registers the v-reveal directive globally, and seeds SSR-safe defaults from nuxt.config.ts on the client.

Picking an adapter is the only decision — install @macrulez/inview-vue for Vue (or let @macrulez/inview-nuxt pull it in), or @macrulez/inview-react for React. The underlying engines, and the options/behavior they expose, stay the same either way.