How to build an accessible InfiniteScroll in React

The trigger is a real button, not a bare sentinel, so the next page is reachable by keyboard and by screen reader rather than only by scrolling — the standing accessibility complaint against scroll-only infinite lists. The IntersectionObserver activates the same code path the button does, so pointer and assistive-technology users converge on identical behaviour. While a page is in flight the button is replaced by a role="status" region carrying the loading string from the i18n catalog, which announces politely. Re-entry is guarded on a loading flag, so a sentinel still in view after a short page cannot loop. Because the component requires client JavaScript, server-render the first page as real content rather than relying on it.

When to use a InfiniteScroll

When not to use it

Keyboard interactions

Role button, verified at WCAG 2.2-AA.

Common mistakes

Avoid: Leaving `disabled` false after the last page

Prefer: Pass `disabled={!hasMore}` so the observer disconnects

A sentinel that stays in view keeps re-firing `onLoadMore` against an exhausted source

Avoid: Rendering it outside the element that scrolls

Prefer: Place it as the last child of the scrolling region

The observer watches the sentinel against the nearest scroll root; outside it, intersection never changes

Example

<InfiniteScroll onLoadMore={loadNextPage} />

See the full InfiniteScroll reference →