Infinite List Sentinel
Sentinel-based pagination with the stall fixed — the bug where a short page leaves the sentinel on screen and loading stops.
- Category
- Patterns
- Added
- 2026-09-21
- Deps
- none
- Updated
- 2026-09-21
This is a viberdy Pro component
The live preview, the props panel and the write-up are open to everyone. The full source, the AI prompt and CLI install are part of Pro.
scroll or press load more
Props
"use client";
import { useEffect, useRef, useState } from "react";
export function InfiniteListSentinel({
pageSize = 12,
total = 60,
latency = 500,
prefetchPx = 200,
load,
}: {
pageSize?: number;
total?: number;
latency?: number;
/** How far below the fold to start loading. */
prefetchPx?: number;
/** Your real fetch. Omitted, a local stand-in is used. */
load?: (offset: number, limit: number) => Promise<string[]>;
// ---------------------------------------------------------------
// Full source available with a viberdy Pro subscription.
// https://viberdy.dev/pricing
// ---------------------------------------------------------------
}About this pattern
Sentinel pagination has two failure modes that are exact opposites, and shipping a fix for one usually introduces the other. The obvious one is the double fetch: two intersection callbacks can land in the same tick and both read the same stale loading flag out of the closure, so the guard has to be a ref that updates synchronously rather than a piece of state. The subtle one — and the one almost everybody ships — is the stall. IntersectionObserver reports threshold CROSSINGS, not a continuing condition, so if a page of rows happens to be short enough that the sentinel is still on screen once they render, no further callback ever arrives and loading stops dead halfway down a list that has plenty more. The fix is to ask the question again after every load: attach a fresh short-lived observer and, if the sentinel is still intersecting, fetch again. Two more things separate this from the usual version. There is a real end condition, so the list stops requesting empty pages forever. And there is always a real Load more button alongside the sentinel, because infinite scroll with no manual trigger is a trap — there is no keyboard gesture for “scroll until more appears”, and anything below the list becomes permanently unreachable. New rows are announced through a polite live region, since appending silently just makes the page longer with no indication that anything happened.
Curator’s note
The stall is the interesting bug: IntersectionObserver fires on crossings, so a short page that leaves the sentinel visible never fires again.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Draggable Sortable List
patternsA drag-handle-driven list that reorders with a smooth layout swap.
Link Peek Preview
patternsAn index list that floats a velocity-tilted preview card beside the pointer.
Spotlight List Focus
patternsA list that dims and blurs every row except the one under the pointer.