12k
All articles

Complete guide to infinite scrolling in React

Build infinite scroll in React with a package or a custom IntersectionObserver hook. Handle performance, loading states, and edge cases effectively.

OpenReplay Team
OpenReplay Team
Complete guide to infinite scrolling in React

Infinite scrolling is a UX pattern where new content loads as users scroll down a page. Common in social feeds, timelines, and news apps, it’s a smooth way to explore large datasets without using pagination buttons. In this guide, you’ll learn how to build infinite scroll in React using both a third-party library and a custom hook.

Key Takeaways

  • Learn 2 practical ways to implement infinite scrolling in React
  • Use a custom hook with IntersectionObserver or a third-party package
  • Handle loading states, pagination, and empty results

What is infinite scroll?

Infinite scroll loads new data as the user scrolls near the end of a list. It removes the need for pagination and creates a continuous browsing experience. You’ll see it in products like Instagram, Twitter, and Reddit.

Method 1: Using a package (react-infinite-scroll-component)

The easiest way to implement infinite scroll in React is with a well-tested package.

Step 1: Install

npm install react-infinite-scroll-component

Step 2: Basic usage example

import InfiniteScroll from 'react-infinite-scroll-component';

function Feed() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);

  const fetchMoreData = async () => {
    const res = await fetch(`/api/posts?page=${page}`);
    const newItems = await res.json();
    if (newItems.length === 0) setHasMore(false);
    else {
      setItems(prev => [...prev, ...newItems]);
      setPage(prev => prev + 1);
    }
  };

  return (
    <InfiniteScroll
      dataLength={items.length}
      next={fetchMoreData}
      hasMore={hasMore}
      loader={<h4>Loading...</h4>}
      endMessage={<p>No more results</p>}
    >
      {items.map(item => <div key={item.id}>{item.title}</div>)}
    </InfiniteScroll>
  );
}

Pros

  • Quick to set up
  • Built-in loading and end states

Cons

  • Less control over scroll logic
  • Adds package dependency

Method 2: Custom hook with IntersectionObserver

This method gives you full control and no extra dependencies.

Step 1: Create a hook

function useInfiniteScroll(callback, ref) {
  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) callback();
    });
    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, [callback, ref]);
}

Step 2: Use in a component

function Feed() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const sentinelRef = useRef(null);

  const loadMore = async () => {
    if (!hasMore) return;
    const res = await fetch(`/api/posts?page=${page}`);
    const newItems = await res.json();
    if (newItems.length === 0) setHasMore(false);
    else {
      setItems(prev => [...prev, ...newItems]);
      setPage(prev => prev + 1);
    }
  };

  useInfiniteScroll(loadMore, sentinelRef);

  return (
    <div>
      {items.map(item => <div key={item.id}>{item.title}</div>)}
      <div ref={sentinelRef} style={{ height: 1 }} />
    </div>
  );
}

Pros

  • Fully customizable
  • Works with any scroll logic or API

Cons

  • Requires more setup
  • IntersectionObserver not supported in IE11

Real-world patterns and tips

  • Debounce fetch calls to avoid overloading APIs
  • Show a spinner or skeleton loader for better UX
  • Add retry logic for network errors
  • Handle edge cases like zero results or empty pages
if (items.length === 0 && !hasMore) {
  return <p>No posts found.</p>;
}

Performance considerations

  • Use libraries like react-window for virtualization when rendering thousands of items
  • Memoize item components to prevent re-renders
  • Clean up observers properly to avoid memory leaks
useEffect(() => {
  const observer = new IntersectionObserver(...);
  return () => observer.disconnect();
}, []);

Conclusion

Infinite scroll is a common feature in modern UIs. Whether you prefer the simplicity of a package or the control of a custom hook, React makes both approaches accessible. Always consider performance and loading feedback when implementing infinite scroll.

FAQs

What’s the easiest way to implement infinite scroll in React?

Using a library like `react-infinite-scroll-component` gives you a fast and convenient setup.

How can I stop loading when there's no more data?

Set a `hasMore` flag to false when the API returns an empty array.

Does infinite scroll hurt performance?

It can, if you're rendering too many items. Use virtualization tools like `react-window` to manage large lists.

How do I add infinite scroll only when near the bottom?

Use `IntersectionObserver` to trigger data loading when a bottom `div` (sentinel) comes into view.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers

We use cookies to improve your experience. By using our site, you accept cookies.