Hooks

useIntersectionObserver

Observe whether an element intersects the viewport (or a custom root) via IntersectionObserver, with automatic cleanup. Returns reactive isIntersecting for simple cases, or accepts a callback for full entry access. The target may be a reactive getter. SSR-safe.

Basic visibility

Read io.isIntersecting directly - no callback needed.

Not visible
<script lang="ts">
  import { useIntersectionObserver } from 'sv5ui';

  let el = $state<HTMLDivElement>();
  const io = useIntersectionObserver(() => el);
</script>

<div bind:this={el} class="rounded border p-6 text-center">
  {io.isIntersecting ? 'Visible in viewport' : 'Not visible'}
</div>

Lazy reveal

Use rootMargin to trigger the callback before the element enters the viewport. Ideal for lazy-loading images or infinite scroll.

Scroll into view to reveal
<script lang="ts">
  import { useIntersectionObserver } from 'sv5ui';

  let img = $state<HTMLImageElement>();
  let loaded = $state(false);

  useIntersectionObserver(
    () => img,
    (entry) => {
      if (entry.isIntersecting && !loaded) {
        loaded = true;
      }
    },
    { rootMargin: '200px' } // start loading 200px before entering viewport
  );
</script>

<img
  bind:this={img}
  src={loaded ? '/real-image.jpg' : undefined}
  alt="Lazy loaded"
  class="h-48 w-full rounded object-cover bg-surface-container"
/>

Threshold & rootMargin

Pass threshold to fire at specific intersection ratios (0–1), and rootMargin to shrink or grow the root's effective bounding box.

See code - logs intersectionRatio to console.

<script lang="ts">
  import { useIntersectionObserver } from 'sv5ui';

  let el = $state<HTMLDivElement>();

  useIntersectionObserver(
    () => el,
    (entry) => {
      console.log('ratio:', entry.intersectionRatio);
    },
    {
      threshold: [0, 0.25, 0.5, 0.75, 1],  // fire at each 25% step
      rootMargin: '-10% 0px'               // shrink root by 10% top/bottom
    }
  );
</script>

<div bind:this={el} class="h-32 rounded border p-4">Scroll to observe</div>

Parameters

ParameterTypeDefault
targetElement | (() => Element | null) | null-
callback(entry: IntersectionObserverEntry) => voidundefined
optionsIntersectionObserverInitundefined

Return

PropertyTypeDefault
isIntersectingbooleanfalse