Web Development  

How to Implement Infinite Scroll in React Without Performance Issues

Introduction

Infinite scroll is a popular feature in modern web applications where content loads automatically as users scroll down the page. It improves user experience by removing pagination and making browsing smooth and continuous. You often see infinite scrolling in social media feeds, e-commerce websites, and blog platforms.

However, if not implemented correctly, infinite scroll can cause performance issues like slow rendering, high memory usage, and poor user experience. In this article, you will learn how to implement infinite scroll in React without performance issues using simple language, practical examples, and production-ready techniques.

What is Infinite Scroll?

Infinite scroll is a technique where new data is loaded automatically when the user reaches the bottom of the page.

Instead of clicking “Next Page,” users keep scrolling and content keeps loading.

Why Use Infinite Scroll?

  • Better user engagement

  • Smooth browsing experience

  • Reduces clicks and navigation

  • Ideal for large datasets

Challenges in Infinite Scroll

  • Too many API calls

  • Slow rendering

  • Memory leaks

  • Duplicate data loading

  • Poor SEO if not handled properly

Basic Approach

The core idea:

  1. Detect when user reaches bottom

  2. Fetch more data from API

  3. Append new data to existing list

Step 1: Setup React Project

npx create-react-app infinite-scroll-app
cd infinite-scroll-app
npm start

Step 2: Create State and Fetch Data

import { useEffect, useState } from 'react';

function App() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);

  const fetchData = async () => {
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts?_page=${page}`);
    const data = await res.json();
    setItems(prev => [...prev, ...data]);
  };

  useEffect(() => {
    fetchData();
  }, [page]);

  return (
    <div>
      {items.map(item => (
        <p key={item.id}>{item.title}</p>
      ))}
    </div>
  );
}

export default App;

Step 3: Detect Scroll Position

useEffect(() => {
  const handleScroll = () => {
    if (window.innerHeight + document.documentElement.scrollTop >= document.documentElement.offsetHeight - 100) {
      setPage(prev => prev + 1);
    }
  };

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

Problem with Basic Approach

This method can cause:

  • Too many re-renders

  • Frequent API calls

  • Performance lag

So we need optimization.

Step 4: Use Intersection Observer (Best Practice)

Instead of listening to scroll events, use Intersection Observer.

import { useRef, useCallback } from 'react';

const observer = useRef();

const lastItemRef = useCallback(node => {
  if (observer.current) observer.current.disconnect();

  observer.current = new IntersectionObserver(entries => {
    if (entries[0].isIntersecting) {
      setPage(prev => prev + 1);
    }
  });

  if (node) observer.current.observe(node);
}, []);

Attach to last item:

{items.map((item, index) => {
  if (index === items.length - 1) {
    return <p ref={lastItemRef} key={item.id}>{item.title}</p>;
  }
  return <p key={item.id}>{item.title}</p>;
})}

Step 5: Prevent Multiple API Calls

Add loading state:

const [loading, setLoading] = useState(false);

const fetchData = async () => {
  if (loading) return;
  setLoading(true);

  const res = await fetch(...);
  const data = await res.json();

  setItems(prev => [...prev, ...data]);
  setLoading(false);
};

Step 6: Stop When No More Data

const [hasMore, setHasMore] = useState(true);

if (data.length === 0) {
  setHasMore(false);
}

Step 7: Use Virtualization for Large Lists

Rendering thousands of items can slow down UI.

Use libraries like:

  • react-window

  • react-virtualized

Example:

import { FixedSizeList as List } from 'react-window';

<List
  height={500}
  itemCount={items.length}
  itemSize={50}
>
  {({ index, style }) => (
    <div style={style}>{items[index].title}</div>
  )}
</List>

Step 8: Add Debouncing or Throttling

To reduce frequent calls:

import debounce from 'lodash.debounce';

Step 9: Improve UX

  • Show loading spinner

  • Show "No more data" message

  • Add skeleton loaders

SEO Consideration

Infinite scroll can affect SEO.

Solutions:

  • Add pagination fallback

  • Use server-side rendering (Next.js)

  • Provide crawlable links

Common Mistakes

  • Not cleaning event listeners

  • Missing loading state

  • Duplicate API calls

  • Rendering too many items

Difference Between Pagination and Infinite Scroll

FeaturePaginationInfinite Scroll
NavigationManualAutomatic
UXClick-basedSmooth
PerformanceStableNeeds optimization
SEOBetterNeeds extra work

Best Practices

  • Use Intersection Observer

  • Implement virtualization

  • Avoid unnecessary re-renders

  • Cache API responses

  • Handle errors properly

Conclusion

Infinite scroll is a powerful feature that enhances user experience when implemented correctly. By using modern techniques like Intersection Observer, virtualization, and proper state management, you can avoid performance issues and build fast, scalable React applications.

With the right approach, infinite scroll can make your application more engaging, responsive, and user-friendly.