Skip to main content

Usage

Tracks whether an element is visible in the viewport, useful for lazy loading, animations, and infinite scroll.
import { useIntersectionObserver } from '@kivora/react';

function LazyImage({ src }: { src: string }) {
  const { ref, isIntersecting } = useIntersectionObserver({ once: true });

  return (
    <div ref={ref}>
      {isIntersecting ? (
        <img src={src} alt="Lazy loaded" />
      ) : (
        <div className="placeholder">Loading...</div>
      )}
    </div>
  );
}

Parameters

options
UseIntersectionObserverOptions
Configuration object for the intersection observer

Returns

ref
RefObject<HTMLElement | null>
Attach this ref to the element you want to observe
isIntersecting
boolean
true when the element is intersecting the viewport
entry
IntersectionObserverEntry | null
The raw IntersectionObserverEntry, or null before first observation

Examples

Fade in animation on scroll

import { useIntersectionObserver } from '@kivora/react';

function FadeIn({ children }: { children: React.ReactNode }) {
  const { ref, isIntersecting } = useIntersectionObserver({
    once: true,
    threshold: 0.5,
  });

  return (
    <div
      ref={ref}
      style={{
        opacity: isIntersecting ? 1 : 0,
        transform: isIntersecting ? 'translateY(0)' : 'translateY(20px)',
        transition: 'opacity 0.5s, transform 0.5s',
      }}
    >
      {children}
    </div>
  );
}

Infinite scroll

import { useIntersectionObserver } from '@kivora/react';
import { useEffect } from 'react';

function InfiniteList({ items, loadMore }: { items: any[]; loadMore: () => void }) {
  const { ref, isIntersecting } = useIntersectionObserver({
    rootMargin: '100px',
  });

  useEffect(() => {
    if (isIntersecting) {
      loadMore();
    }
  }, [isIntersecting, loadMore]);

  return (
    <div>
      {items.map((item, i) => (
        <div key={i}>{item}</div>
      ))}
      <div ref={ref}>Loading more...</div>
    </div>
  );
}

Track visibility percentage

import { useIntersectionObserver } from '@kivora/react';

function VisibilityTracker() {
  const { ref, entry } = useIntersectionObserver({
    threshold: [0, 0.25, 0.5, 0.75, 1],
  });

  const visibilityPercentage = entry
    ? Math.round(entry.intersectionRatio * 100)
    : 0;

  return (
    <div ref={ref} style={{ height: '200px', background: '#f0f0f0' }}>
      Visible: {visibilityPercentage}%
    </div>
  );
}

Lazy load with placeholder

import { useIntersectionObserver } from '@kivora/react';

function LazyComponent() {
  const { ref, isIntersecting } = useIntersectionObserver({
    once: true,
    rootMargin: '200px',
  });

  return (
    <div ref={ref} style={{ minHeight: '400px' }}>
      {isIntersecting ? (
        <ExpensiveComponent />
      ) : (
        <div className="skeleton-loader" />
      )}
    </div>
  );
}

Build docs developers (and LLMs) love