v2.0.0
Svelte 5 Runes support
Breaking Changes
- Port
IntersectionObserverandMultipleIntersectionObserverto Svelte 5 runes; requiressvelte@^5in runes mode - Replace the default
<slot>with achildrensnippet — use{#snippet children({ intersecting, entry, observer })}instead oflet:intersecting/let:entry/let:observer(andlet:elementIntersections/let:elementEntries/let:observerforMultipleIntersectionObserver) - Replace
on:observe/on:intersectdispatched events withonobserve/onintersectcallback props, called directly with theIntersectionObserverEntry(or{ entry, target }forMultipleIntersectionObserver) instead of aCustomEvent use:intersectnow listens viaonobserve/onintersectattributes instead ofon:observe/on:intersect(the action itself still dispatches nativeCustomEvents)- Rename
src/intersect.js/src/intersect.d.tstosrc/intersect.svelte.js/src/intersect.svelte.d.ts
Migration guide
Svelte 5 runes mode is now required
v1 worked with Svelte 3, 4, and 5 (in legacy/non-runes mode). v2 only works with Svelte 5 in runes mode. If your project hasn't migrated to runes yet, stay on v1 until it has.
Slot props → children snippet
The default <slot> is now a children snippet. Replace let:xxx directives with a {#snippet children({ ... })} block.
<!-- Before (v1) -->
<IntersectionObserver {element} let:intersecting let:entry let:observer>
<div bind:this={element}>
{intersecting ? "In view" : "Not in view"}
</div>
</IntersectionObserver>
<!-- After (v2) -->
<IntersectionObserver {element}>
{#snippet children({ intersecting, entry, observer })}
<div bind:this={element}>
{intersecting ? "In view" : "Not in view"}
</div>
{/snippet}
</IntersectionObserver>If you only bind to intersecting/entry/observer with bind: (rather than reading the slot props), you don't need the children snippet at all — plain child markup still works:
<!-- Still valid in v2 -->
<IntersectionObserver {element} bind:intersecting>
<div bind:this={element}>Hello world</div>
</IntersectionObserver>The same applies to MultipleIntersectionObserver, whose slot props were elementIntersections, elementEntries, and observer:
<!-- Before (v1) -->
<MultipleIntersectionObserver {elements} let:elementIntersections>
<div bind:this={ref1}>{elementIntersections.get(ref1) ? "✓" : "✗"}</div>
</MultipleIntersectionObserver>
<!-- After (v2) -->
<MultipleIntersectionObserver {elements}>
{#snippet children({ elementIntersections })}
<div bind:this={ref1}>{elementIntersections.get(ref1) ? "✓" : "✗"}</div>
{/snippet}
</MultipleIntersectionObserver>on:observe/on:intersect → onobserve/onintersect callback props
Component events are no longer dispatched via createEventDispatcher. Listen with callback props instead — and note the callback receives the IntersectionObserverEntry directly, not a CustomEvent wrapping it.
<!-- Before (v1) -->
<IntersectionObserver
{element}
on:observe={(e) => console.log(e.detail.isIntersecting)}
on:intersect={(e) => console.log(e.detail)}
>
<div bind:this={element}>Hello world</div>
</IntersectionObserver>
<!-- After (v2) -->
<IntersectionObserver
{element}
onobserve={(entry) => console.log(entry.isIntersecting)}
onintersect={(entry) => console.log(entry)}
>
<div bind:this={element}>Hello world</div>
</IntersectionObserver>For MultipleIntersectionObserver, the callback receives { entry, target } directly instead of e.detail:
<!-- Before (v1) -->
<MultipleIntersectionObserver
{elements}
on:observe={(e) => console.log(e.detail.target, e.detail.entry)}
/>
<!-- After (v2) -->
<MultipleIntersectionObserver
{elements}
onobserve={({ target, entry }) => console.log(target, entry)}
/>use:intersect action: on:observe/on:intersect → onobserve/onintersect
The action itself is unchanged — it still dispatches native CustomEvents on the element — but the on: directive is gone in Svelte 5, so listen with the plain event attributes instead. e.detail is unchanged.
<!-- Before (v1) -->
<div
use:intersect={{ once: true }}
on:observe={(e) => (intersecting = e.detail.isIntersecting)}
>
Hello world
</div>
<!-- After (v2) -->
<div
use:intersect={{ once: true }}
onobserve={(e) => (intersecting = e.detail.isIntersecting)}
>
Hello world
</div>Type imports
If you imported the action's types via a subpath (rather than the root package export), update the path to match the renamed file:
// Before (v1)
import type { IntersectActionOptions } from "svelte-intersection-observer/intersect";
// After (v2)
import type { IntersectActionOptions } from "svelte-intersection-observer/intersect.svelte";The root package export (import { intersect } from "svelte-intersection-observer") is unaffected.