Features
- Add
createIntersectionGroupfor shared-observer list usage (#138) - Add
createIntersectionObserverrune-based composable (#139)
Fixes
- Guard against missing
IntersectionObserversupport (#137) IntersectionObserver: unobserve the previous element whenelementbecomesnull(#143)MultipleIntersectionObserver: unobserve elements whenelementsbecomes empty (#142)createIntersectionGroup: guard against missingIntersectionObserversupport (#149)
Performance
MultipleIntersectionObserver: use Set-based diffing for theelementsarrayIntersectionObserver: avoid recreating the observer for value-equalthreshold,root, orrootMargin(#141)
Usage guide
createIntersectionObserver composable
createIntersectionObserver gets you reactive intersection state without wrapping markup in a component or wiring up your own onobserve callback: call it in <script> for intersecting/entry getters, then apply attach to the node with {@attach}.
<script>
import { createIntersectionObserver } from "svelte-intersection-observer";
const observer = createIntersectionObserver(() => ({ threshold: 0.5 }));
</script>
<div {@attach observer.attach}>
{observer.intersecting ? "In view" : "Not in view"}
</div>It takes the same options as intersectAttachment (a function returning the options object) and reuses its underlying observer logic — intersecting, entry, and attach are the only things it hands back.
createIntersectionGroup for #each lists
A bare intersect/intersectAttachment inside an #each block creates one native IntersectionObserver per iteration — for a long list, that's N observers instead of 1. createIntersectionGroup fixes this for the action/attachment API: call it once to create a group, then call group.attach(...) once per element to get an attachment that shares a single underlying observer across the whole group.
<script>
import { createIntersectionGroup } from "svelte-intersection-observer";
let groupItems = $state(
Array.from({ length: 5 }, (_, i) => ({ id: i, intersecting: false })),
);
const group = createIntersectionGroup();
</script>
{#each groupItems as item (item.id)}
<div
class:intersecting={item.intersecting}
{@attach group.attach({
onobserve: (entry) => (item.intersecting = entry.isIntersecting),
})}
>
Item {item.id}
</div>
{/each}root/rootMargin/threshold configure the one shared observer, so they're passed once to createIntersectionGroup itself rather than per element; once, skip, onobserve, and onintersect are the only options that make sense per element, so those are what group.attach(...) accepts.
Choosing between MultipleIntersectionObserver and createIntersectionGroup
Both share one observer across many elements. Pick based on how you want to wire it up:
- Use
MultipleIntersectionObserverwhen you're fine wrapping the list in a component and want a ready-madeelementIntersectionsmap handed to you via thechildrensnippet. - Use
createIntersectionGroupwhen you'd rather attach directly to each element with{@attach}, no extra component, and are happy tracking intersection state on your own per-item objects.
Type imports
import type {
IntersectGroupNodeOptions,
IntersectGroupSharedOptions,
IntersectionGroup,
} from "svelte-intersection-observer/intersect.svelte";
import type { IntersectionObserverState } from "svelte-intersection-observer/create-intersection-observer.svelte";
import {
createIntersectionGroup,
createIntersectionObserver,
} from "svelte-intersection-observer";