FrontendReactInfinite ScrollPerformanceIntersection ObserverFrontend

React Infinite Scroll with Intersection Observer API

Implement performant infinite scrolling in React using the Intersection Observer API without any third-party library.

Abdur Razzak

Abdur Razzak

Full-Stack Web Developer

December 5, 2024 9 min read

Why Infinite Scroll?

Infinite scroll loads more content as the user approaches the bottom of the page, providing a seamless browsing experience. It is commonly used in social feeds, product listings, and search results where paginated navigation would interrupt the flow. The Intersection Observer API is the modern, performant way to detect when an element enters the viewport — far better than scroll event listeners, which fire hundreds of times per second.

Setting Up the Intersection Observer

Create a ref for a sentinel element (an empty div) at the bottom of your list. In a useEffect, create an IntersectionObserver that fires a callback when the sentinel enters the viewport. Inside the callback, trigger your load-more function. Return a cleanup function that calls observer.disconnect() to prevent memory leaks. The observer fires once when the sentinel becomes visible, not continuously like a scroll event.

Building the Custom useInfiniteScroll Hook

Encapsulate the IntersectionObserver logic in a custom hook that accepts a callback and returns a ref to attach to the sentinel element. The hook handles observer creation, cleanup, and the connection to the sentinel ref. Components that need infinite scroll just call useInfiniteScroll(loadMore) and attach the returned ref to a div at the bottom of their list.

Fetching Paginated Data

Use TanStack Query's useInfiniteQuery for server-side paginated data. It manages the array of pages, the next page cursor, and loading states. The fetchNextPage function loads the next chunk of data and appends it to the existing pages. Flatten the pages array for rendering: data.pages.flatMap(page => page.items). Show a loading spinner when isFetchingNextPage is true.

Handling Edge Cases

Handle three edge cases: empty state (show an empty message when no items exist), end of data (hide the sentinel or show a 'You have reached the end' message when hasNextPage is false), and error state (show a retry button if fetchNextPage fails). Disable the observer when loading is in progress to prevent duplicate fetch calls from firing before the first request resolves.

Performance Optimizations

For very long lists (thousands of items), implement virtual scrolling with @tanstack/react-virtual. Virtual scrolling renders only the items currently visible in the viewport, dramatically reducing DOM nodes and improving scroll performance. Combine virtual scrolling with infinite query for an efficient, scalable list that handles any amount of data without performance degradation.

Share this article

All posts
#React#Infinite Scroll#Performance#Intersection Observer#Frontend
Abdur Razzak — Full Stack Web Developer

Free Consultation

Got a Project Idea? Let's Talk.