Implementing Infinite Scroll in Next.js with SSG Without Any Library

Kawan Idrees - Jan 28 - - Dev Community

Infinite scroll is a popular feature in modern web applications, providing a seamless way for users to access more content without the disruption of pagination. In the context of a Next.js application using Static Site Generation (SSG), implementing infinite scroll can be particularly interesting. Next.js's SSG allows for faster load times and better SEO, but it generally pre-renders pages at build time, which seems at odds with the dynamic nature of infinite scroll. However, with a bit of creativity, we can efficiently combine SSG with client-side data fetching to achieve this.

Understanding the Challenge
Static Site Generation (SSG) in Next.js pre-renders pages at build time. This is fantastic for performance and SEO but poses a challenge for features like infinite scroll, which relies on dynamically loading content. The key is to pre-render a significant initial portion of the content statically and then dynamically load additional content as needed.

Initial Setup with getStaticProps
First, we pre-render an initial set of data using getStaticProps. For this example, we'll use the JSONPlaceholder's /photos endpoint to simulate fetching data.

// pages/index.js
export async function getStaticProps() {
  const res = await fetch('https://jsonplaceholder.typicode.com/photos?_limit=10');
  const initialData = await res.json();
  return { props: { initialData } };
}

Enter fullscreen mode Exit fullscreen mode

Implementing Infinite Scroll on the Client Side
Next, we set up a mechanism to detect when the user has scrolled to the bottom of the page, triggering additional data fetches.


import Image from "next/image";
import { useCallback, useEffect, useState } from "react";

const IndexPage = ({ initialData }) => {
  const [data, setData] = useState(initialData);
  const [page, setPage] = useState(1);
  const [isLoading, setIsLoading] = useState(false); // New state for loading
  const loadMoreData = async () => {
    setIsLoading(true);
    const moreData = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=10&_page=${page + 1}`)
      .then(res => res.json());
    setData(currentData => [...currentData, ...moreData]);
    setPage(currentPage => currentPage + 1);
    setIsLoading(false);
  };

  const onScroll = useCallback(async () => { 
    if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100 && !isLoading) {
      await loadMoreData();
    }
  }, [isLoading, page]); // Dependencies

  useEffect(() => {
    window.addEventListener('scroll', onScroll);
    return () => window.removeEventListener('scroll', onScroll);
  }, [onScroll]); 

  return (
    <div
      style={{ display: "flex", flexDirection: "column", alignItems: "center" }}
    >
      {data.map((item) => (
        <div
          key={item.id}
          style={{
            marginBottom: "20px",
            position: "relative",
            width: "400px",
            height: "400px",
            display: "flex",
            flexDirection: "column",
            alignItems: "center",
          }}
        >
          <Image
            src={item.url}
            alt={item.title}
            fill
            style={{ objectFit: "contain" }}
          />
        </div>
      ))}
      {isLoading && <p>Loading more images...</p>} {/* Loading indicator */}
    </div>
  );
};

export default IndexPage;


Enter fullscreen mode Exit fullscreen mode

Adding a Loading Indicator for Better UX
To improve the user experience, we include a loading state to indicate when more data is being fetched.

Key Considerations
1-No External Library: This implementation relies solely on Next.js and native JavaScript, avoiding additional dependencies.
2-Optimizing Performance: Consider debouncing the scroll event and adding a throttle mechanism to reduce the number of API calls during rapid scrolling.
3-Handling the End of Data: Implement logic to handle the scenario when there is no more data to load.
4-Styling and UX: The loading indicator and content styling can be enhanced for a more engaging user experience.

don't forget to adjust your next.config.js because the remote images has to be configured in next js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,


  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'via.placeholder.com',
        port: '',
        pathname: '/**',
      },
    ],
  },
};

export default nextConfig;

Enter fullscreen mode Exit fullscreen mode

Conclusion
Combining SSG with client-side data fetching in Next.js offers a powerful approach to implementing features like infinite scroll. While SSG optimizes initial load performance and SEO, client-side fetching ensures dynamic, interactive user experiences. This blend of static and dynamic content delivery represents the versatility and power of modern web development frameworks like Next.js.

. . . . . . . . . .