Editor’s note: This article was last updated by Emmanuel Odioko on 27 August 2024 to incorporate the popular scroll to top functionality when implementing infinite scroll as well as to showcase the use of libraries like react-window-infinite-loader.
Infinite scrolling is a web design technique that loads content continuously as the user scrolls down the page. This intuitive feature eliminates the need for traditional pagination. Instead of navigating through so many pages, users can non-stop scroll to view more content, making the experience more engaging.
This approach is widely used in social media platforms like Instagram, X, and TikTok, where users can endlessly browse through feeds of images and videos without interruption.
In this article, we’ll explore three unique approaches to implementing infinite scrolling in React applications:
Let’s get started!
Throughout this article, we’ll build on a consistent foundation to implement the various infinite scrolling techniques. For each method, we’ll adapt the following steps and code snippets.
First, we’ll set up the initial state for our component. This includes the list of items to display, the necessary loading and error indicators, and a variable to keep track of the current page number:
import React, { useState, useEffect, useRef } from 'react'; const InfiniteScrollExample = () => { const [items, setItems] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [page, setPage] = useState(1); // ... rest of the component };
Next, we’ll create a function to fetch data from an API or another data source, increment the page number, and update the state with the fetched items. Additionally, we’ll handle any errors during the data fetching process:
const fetchData = async () => { setIsLoading(true); setError(null); try { const response = await fetch(`https://api.example.com/items?page=${page}`); const data = await response.json(); setItems(prevItems => [...prevItems, ...data]); setPage(prevPage => prevPage + 1); } catch (error) { setError(error); } finally { setIsLoading(false); } };
fetchData
on component mountLastly, we’ll use the useEffect
Hook to call the fetchData
function when the component mounts initially:
useEffect(() => { fetchData(); }, []);
These foundational steps will be present in all the techniques we discuss in this article. We’ll modify and expand upon them as a base.
Building the entire infinite scroll implementation from scratch involves handling the scroll event, loading more data, and updating the state in your React application. This approach provides you with full control over customization and functionality.
Extending our basic setup, we’ll first create a function to handle the scroll
event. This function will check if the user has reached the bottom of the page and call fetchData
if necessary. We’ll add a scroll
event listener to the window
object and remove it when the component is unmounted:
const handleScroll = () => { if (window.innerHeight + document.documentElement.scrollTop !== document.documentElement.offsetHeight || isLoading) { return; } fetchData(); }; useEffect(() => { window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [isLoading]);
Finally, we render the items, the loading indicator, and any error messages within the component:
return ( <div> <ul> {items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> {isLoading && <p>Loading...</p>} {error && <p>Error: {error.message}</p>} </div> );
With that, we have a fully functional infinite scroll implementation built from scratch. This approach allows for extensive customization and more control over functionality. However, it may be more time-consuming and require more maintenance than using an existing library or component.
Using an existing infinite scroll library or component can save time and effort as you leverage pre-built and pre-tested solutions while retaining customization options. We will cover two of these libraries in this section:
react-infinite-scroll-component is a popular library for implementing infinite scrolling in React. Let’s learn how to use this library to create infinite scrolling in our React application.
First, install react-infinite-scroll-component using npm or Yarn:
npm install react-infinite-scroll-component or yarn add react-infinite-scroll-component
Then, we’ll extend our basic setup, import the InfiniteScroll
component from the library, and wrap the list of items in it. Configure the component by passing the necessary props like dataLength
, next
, hasMore
, and loader
:
import InfiniteScroll from 'react-infinite-scroll-component'; // ... return ( <div> <InfiniteScroll dataLength={items.length} next={fetchData} hasMore={true} // Replace with a condition based on your data source loader={<p>Loading...</p>} endMessage={<p>No more data to load.</p>} > <ul> {items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> </InfiniteScroll> {error && <p>Error: {error.message}</p>} </div> );
With that, we’ve implemented infinite scrolling in our React application. We didn’t use scroll
handling because react-infinite-scroll-component handles that for us.
The react-infinite-scroll-component library offers a faster and more streamlined implementation process but still provides customization options, like scroll height and scroll overflow. However, you should keep in mind the trade-off of introducing additional dependencies to your project.
Second on our list is the react-window library, which was designed for rendering large lists efficiently, and the react-window-infinite-loader library, which is used to handle infinite scrolling and load more data as the user scrolls.
First, install the react-window-infinite-loader library using npm or Yarn:
# Yarn yarn add react-window-infinite-loader # NPM npm install --save react-window-infinite-loader
Import as seen below:
import { FixedSizeList as List } from "react-window"; import InfiniteLoader from "react-window-infinite-loader";
Now, let’s work on the basic logic. Feel free to copy the code below:
const MyInfiniteScrollComponent = () => { const [items, setItems] = useState([...Array(20).fill().map((_, i) => `Item ${i}`)]); const [hasNextPage, setHasNextPage] = useState(true); const [isNextPageLoading, setIsNextPageLoading] = useState(false); const loadMoreItems = () => { if (isNextPageLoading) return; setIsNextPageLoading(true); // Simulate fetching data setTimeout(() => { const moreItems = [...Array(20).fill().map((_, i) => `Item ${items.length + i}`)]; setItems((prevItems) => [...prevItems, ...moreItems]); if (items.length > 100) { setHasNextPage(false); } setIsNextPageLoading(false); }, 1000); }; const isItemLoaded = (index) => !hasNextPage || index < items.length;
In the code above, we set up our state management. items()
holds the displayed items, hasNextPage()
tracks if there are more items available, and isNextPageLoading()
prevents concurrent fetches. The loadMoreItems()
function simulates fetching and appends new items, updating hasNextPage()
if necessary, while the isItemLoaded()
checks if an item is properly loaded:
const Row = ({ index, style }) => ( <div style={style}>{isItemLoaded(index) ? items[index] : "Loading..."}</div> ); return ( <InfiniteLoader isItemLoaded={isItemLoaded} itemCount={hasNextPage ? items.length + 1 : items.length} loadMoreItems={loadMoreItems} > {({ onItemsRendered, ref }) => ( <List height={400} itemCount={items.length} itemSize={35} onItemsRendered={onItemsRendered} ref={ref} width={300} > {Row} </List> )} </InfiniteLoader> ); };
In the code above, when we combine InfiniteLoader()
and FixedSizeList()
, the component ensures that only the visible items are rendered and new items are loaded as the user scrolls downwards continuously, creating the infinite scroll feature.
The Intersection Observer API is a modern development technique that can detect when elements come into view, thereby triggering content loading for infinite scrolling. The Intersection Observer API observes changes in the intersection of target elements with an ancestor element or the viewport, making it well-suited to implement infinite scrolling.
Extending our basic setup, create a ref
for the observer
target element and set up the Intersection Observer in a useEffect
Hook. When the target element comes into view, call the fetchData
function as follows:
const observerTarget = useRef(null); useEffect(() => { const observer = new IntersectionObserver( entries => { if (entries[0].isIntersecting) { fetchData(); } }, { threshold: 1 } ); if (observerTarget.current) { observer.observe(observerTarget.current); } return () => { if (observerTarget.current) { observer.unobserve(observerTarget.current); } }; }, [observerTarget]);
Then, render the items, loading indicator, error messages, and the observer
target element within the component:
return ( <div> <ul> {items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> {isLoading && <p>Loading...</p>} {error && <p>Error: {error.message}</p>} <div ref={observerTarget}></div> </div> );
By leveraging the Intersection Observer API, we have created an efficient and performant infinite scrolling solution in our React application. This approach offers a modern, browser-native method for detecting when elements come into view, but it may not be supported in all browsers or environments without using a polyfill.
ref
and scrollTop
APIScroll to top is an additional functionality often implemented in infinite scrolling that provides a better user experience. If you’ve been on social media anytime recently, you’re familiar with scroll to top. In X, for example, when you scroll through your for you page (FYP), it never really ends; this is an example of infinite scrolling in action. Then, when you click the home icon in the X navigation menu, it takes you right back to the top.
The home icon in X serves two purposes: to refresh and fetch more data from your FYP and to provide modern scroll to top functionality. For a good user experience, I think all implementations of infinite scroll should have the option to scroll back up to the top of the feed.
To implement this in React, we will need the scrollTop()
property and the useRef()
Hook to have good control of the scroll position. Both of them are a strong mix that comes in handy when implementing features like the scroll to top buttons or dynamically loading content as the user scrolls, as seen in the example where we built the entire implementation from scratch:
"use client" import React, { useRef } from 'react'; function ScrollableComponent() { const scrollableDiv = useRef(null); const scrollToTop = () => { if (scrollableDiv.current) { scrollableDiv.current.scrollTop = 0; } }; return ( <div> <div ref={scrollableDiv} style={{ height: '200px', overflowY: 'scroll' }}> {/* Infinite Scroll content */} </div> <button onClick={scrollToTop}>Scroll to Top</button> </div> ); } export default ScrollableComponent;
In the code above, the scroll to the top is achieved using a ref()
and scrollTop()
, which directly accesses and manipulates the scroll position of the div
through the scrollToTop()
function. With this, we can easily improve our user’s infinite scrolling experience.
Infinite scrolling is a powerful web design technique. It enhances the user experience by progressively loading content as users scroll down a page, thereby eliminating the need for pagination.
In this article, we explored four different approaches for implementing infinite scrolling in React applications. Each technique has its advantages and potential drawbacks, so it’s essential to choose the method that best suits your specific requirements and your users’ needs.
By implementing infinite scrolling in your React applications, you can provide an intuitive and engaging user experience that keeps visitors engaged with your content. I hope you enjoyed this article! Be sure to leave a comment if you have any questions.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
2 Replies to "What is infinite scrolling: Implementing it in React"
Hi,
Thanks,
I followed you & implemented the infinite scroll. I have been facing this issue.
I have set page size 30, So for each API response I receive the 30 data. The problem is, when I open the application in big screen like TV/projector, Initial API call loads the 30 data & no scroll appear due to large screen. But I have 1000 data in Database.
I told team, We could increase the initial page size 100, then they asked suppose after increased size What If the scroll doesn’t appear on window. More over, they want to keep page size 30 only, do not want to change.
How to solve when I open the application in big screen, load the data until scroll appear?
This error occured because you did not specify the context in which the infinite scroll should work. It defaults to the viewport if not set.
The solution is that you pass in the root which is a selector to the options object as shown below.
let options = {
root: document.querySelector(“#scrollArea”),
rootMargin: “0px”,
threshold: 1.0,
};
let observer = new IntersectionObserver(callback, options);