CodeWiz Logo

    CodeWiz

    Creating Infinite Scroll in React/Next.js using Intersection Observer API

    Creating Infinite Scroll in React/Next.js using Intersection Observer API

    21/01/2025

    Introduction

    Have you ever wondered how infinite scroll works on websites like Facebook, Twitter, or Instagram work? In this tutorial, we will learn how to implement infinite scroll in a React or Next.js application using the Intersection Observer API.

    Intersection Observer API

    The Intersection Observer API is a modern web API that allows us to observe changes in the intersection of a target element with an ancestor element or the viewport. This API is commonly used to implement features like lazy loading of images, infinite scroll, and more. Earlier infinite scroll was usually implemented using scroll event listeners, but the Intersection Observer API provides a more efficient way now to detect when an element comes into view.

    Now let's see how we can use this API to implement infinite scroll in a React component.

    Posts Component

    Say we have a Posts component that fetches a list of posts from an API and renders them as a list of cards. We will implement infinite scroll to load more posts as the user scrolls down the page.

    Initially we will start without infinite scroll, we are just displaying a list of posts from a single call to the API.

    
    import React, { useEffect, useState } from "react";
    import { getPosts } from "./actions";
    import Post from "./post"; 
    
    export default function Posts() {
      const [posts, setPosts] = useState<Post[]>([]);
    
      useEffect(() => {
        const fetchPosts = async () => {
          try {
            const response = await getPosts(0); // getPosts calls the API to fetch posts passing page number. We are passing 0 to get the first page data.
            setPosts(response.data);
          } catch (error) {
            console.error("Error fetching posts:", error);
          }
        };
    
        fetchPosts();
      }, []);
    
      return (
        <div>
          {posts.map((post) => (
            <Post key={post.id} post={post} /> // Post displays post data in card format
          ))}
        </div>
      );
    }

    Implementing Infinite Scroll

    To implement infinite scroll, we need to do the following:

    Add State for Pagination details and Loading status

    
    const [page, setPage] = useState<number>(0);
    const [hasMore, setHasMore] = useState<boolean>(true);
    const [isLoading, setIsLoading] = useState(false);

    Use Intersection Observer to Detect Scroll

    If we directly use the Intersection Observer API, we need to handle the observer setup and teardown, which can be complex. Instead, we can use the react-intersection-observer library, which provides a React hook to easily observe elements in the viewport.

    We will use the useInView hook from the react-intersection-observer library to detect when the user has scrolled to the bottom of the list. When the last post is in view, we will load more posts by calling the fetchPosts function with the next page number.

    Here is the final implementation of the Posts component with infinite scroll:

    
    import React, { useEffect, useState } from "react";
    import { getPosts } from "./actions";
    import Post from "./post";
    import { useInView } from "react-intersection-observer";
    
    export default function Posts() {
      const [posts, setPosts] = useState<Post[]>([]);
      const [page, setPage] = useState<number>(0);
      const [hasMore, setHasMore] = useState<boolean>(true);
      const [isLoading, setIsLoading] = useState(false);
      const [scrollTrigger, isInView] = useInView();
    
      const fetchPosts = async (page: number) => {
        setIsLoading(true);
        try {
          const response = await getPosts(page);
          setPosts((prevPosts) => [...prevPosts, ...response.data]);
          setHasMore(response.data.length > 0);
        } catch (error) {
          console.error("Error fetching posts:", error);
        } finally {
          setIsLoading(false);
        }
      };
    
      useEffect(() => {
        fetchPosts(page);
      }, [page]);
    
      useEffect(() => {
        if (isInView && hasMore && !isLoading) {
          setPage((prevPage) => prevPage + 1);
        }
      }, [isInView, hasMore, isLoading]);
    
      return (
        <div>
          {posts.map((post) => (
            <Post key={post.id} post={post} /> // Displays post data in card format
          ))}
          {(hasMore && 
            <div ref={scrollTrigger}>Loading...</div>
            ) || (
            <p className="...">No more posts to load</p>
          )}
      );
    }

    useInView hook returns 2 values, scrollTrigger and isInView. scrollTrigger is a ref that you need to attach to the element you want to observe. In this case, we attach it to a div element at the end of the list of posts. isInView is a boolean value that indicates whether the element with the scrollTrigger ref is in view or not.

    When the scrollTrigger element comes into view, we increment the page state to load more posts. The fetchPosts function is called whenever the page state changes, which fetches the posts for the given page number and appends them to the existing list of posts.

    You can find the complete source code here

    Conclusion

    In this tutorial, we learned how to implement infinite scroll in a React or Next.js application using the Intersection Observer API. By using the react-intersection-observer library, we were able to detect when the user has scrolled to the bottom of the list and load more posts dynamically. This technique can be applied to various scenarios where you need to load content as the user scrolls down the page.

    To stay updated with the latest web development tutorials, follow us on youtube, linked in and medium.

    Video Version

    In the below video, you can find more detailed explanation of the above implementation.